From Jason Daly, "This set of changes reworks the BSP plugin to use TexEnvCombine instead of a GLSL program for doing the texture blending on displaced terrain geometry. After working with it a while, I found this method to be more scene graph friendly (at least until Wojtek gets his VirtualProgram scheme working ;-) )

Note that this technique will require ARB_texture_env_crossbar (or NV_texture_env_combine4), as it mixes two textures in the first texture unit, and just does the lighting in the second.  I doubt this will be a problem for any card that can already handle GLSL programs, though."
This commit is contained in:
Robert Osfield 2009-07-24 15:09:37 +00:00
parent de9ce8a3c0
commit 6af1859bc4
3 changed files with 94 additions and 122 deletions

View File

@ -29,7 +29,7 @@ VBSPGeometry::VBSPGeometry(VBSPData * bspData)
disp_vertex_array = new Vec3Array();
disp_normal_array = new Vec3Array();
disp_texcoord_array = new Vec2Array();
disp_vertex_attr_array = new FloatArray();
disp_vertex_attr_array = new Vec4Array();
// Create a second primitive set for drawing indexed triangles, which is
// the quickest method for drawing the displacement surfaces
@ -276,6 +276,7 @@ void VBSPGeometry::createDispSurface(Face & face, DisplaceInfo & dispInfo)
osg::Vec3 normal;
float u, v;
osg::Vec2 texCoord;
float alphaBlend;
unsigned char edgeBits;
@ -422,9 +423,12 @@ void VBSPGeometry::createDispSurface(Face & face, DisplaceInfo & dispInfo)
// Add the texture coordinate to the array
disp_texcoord_array->push_back(texCoord);
// Get the texture blend parameter for this vertex as well
disp_vertex_attr_array->
push_back(dispVertInfo.alpha_blend / 255.0);
// Get the texture blend parameter for this vertex as well, and
// assign it as the alpha channel for the primary vertex color.
// We'll use a combiner operation to do the texture blending
alphaBlend = dispVertInfo.alpha_blend / 255.0;
disp_vertex_attr_array->push_back(
osg::Vec4f(1.0, 1.0, 1.0, 1.0 - alphaBlend));
}
}
@ -667,14 +671,9 @@ ref_ptr<Group> VBSPGeometry::createGeometry()
geometry->setNormalArray(disp_normal_array.get());
geometry->setNormalBinding(Geometry::BIND_PER_VERTEX);
geometry->setTexCoordArray(0, disp_texcoord_array.get());
geometry->setVertexAttribArray(1, disp_vertex_attr_array.get());
geometry->setVertexAttribBinding(1, Geometry::BIND_PER_VERTEX);
// Add an overall color
color.set(1.0, 1.0, 1.0, 1.0);
colorArray = new Vec4Array(1, &color);
geometry->setColorArray(colorArray.get());
geometry->setColorBinding(Geometry::BIND_OVERALL);
geometry->setTexCoordArray(1, disp_texcoord_array.get());
geometry->setColorArray(disp_vertex_attr_array.get());
geometry->setColorBinding(Geometry::BIND_PER_VERTEX);
// Add our primitive set to the geometry
geometry->addPrimitiveSet(disp_primitive_set.get());

View File

@ -27,7 +27,7 @@ class VBSPGeometry
osg::ref_ptr<osg::Vec3Array> disp_vertex_array;
osg::ref_ptr<osg::Vec3Array> disp_normal_array;
osg::ref_ptr<osg::Vec2Array> disp_texcoord_array;
osg::ref_ptr<osg::FloatArray> disp_vertex_attr_array;
osg::ref_ptr<osg::Vec4Array> disp_vertex_attr_array;
osg::ref_ptr<osg::DrawElementsUInt> disp_primitive_set;
bool doesEdgeExist(int row, int col, int direction,

View File

@ -12,6 +12,8 @@
#include <osg/Texture1D>
#include <osg/Texture2D>
#include <osg/Texture3D>
#include <osg/TexEnv>
#include <osg/TexEnvCombine>
#include <osgDB/Registry>
#include <osgDB/FileUtils>
#include <osgDB/ReadFile>
@ -636,100 +638,6 @@ ref_ptr<Texture> VBSPReader::readTextureFile(std::string textureName)
}
ref_ptr<StateSet> VBSPReader::createBlendShader(Texture * tex1, Texture * tex2)
{
const char * blendVtxShaderCode =
{
"attribute float vBlendParam;\n"
"\n"
"varying float fBlendParam;\n"
"\n"
"void main(void)\n"
"{\n"
" vec3 normal, lightDir;\n"
" vec4 ambient, diffuse;\n"
" float nDotL;\n"
"\n"
" // Simple directional lighting (for now). We're assuming a\n"
" // single light source\n"
" // TODO: This is only used for terrain geometry, so it should be\n"
" // lightmapped\n"
" normal = normalize(gl_NormalMatrix * gl_Normal);\n"
" lightDir = normalize(vec3(gl_LightSource[0].position));\n"
" nDotL = max(dot(normal, lightDir), 0.0);\n"
" ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;\n"
" diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;\n"
"\n"
" // Calculate the vertex color\n"
" gl_FrontColor = 0.1 + ambient + nDotL * diffuse;\n"
"\n"
" // Pass the texture blend parameter through to the fragment\n"
" // shader\n"
" fBlendParam = vBlendParam;\n"
"\n"
" // The basic transforms\n"
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
" gl_TexCoord[0] = vec4(gl_MultiTexCoord0.st, 0.0, 0.0);\n"
"}\n"
};
const char * blendFrgShaderCode =
{
"uniform sampler2D tex1;\n"
"uniform sampler2D tex2;\n"
"\n"
"varying float fBlendParam;\n"
"\n"
"void main(void)\n"
"{\n"
" vec4 tex1Color;\n"
" vec4 tex2Color;\n"
"\n"
" tex1Color = texture2D(tex1, gl_TexCoord[0].st) *\n"
" (1.0 - fBlendParam);\n"
" tex2Color = texture2D(tex2, gl_TexCoord[0].st) * fBlendParam;\n"
"\n"
" gl_FragColor = gl_Color * (tex1Color + tex2Color);\n"
"}\n"
};
// Create the stateset
StateSet * stateSet = new StateSet();
// Add the two textures
stateSet->setTextureAttributeAndModes(0, tex1, StateAttribute::ON);
stateSet->setTextureAttributeAndModes(1, tex2, StateAttribute::ON);
// Create the vertex and fragment shaders
Shader * blendVtxShader = new Shader(Shader::VERTEX);
blendVtxShader->setShaderSource(blendVtxShaderCode);
Shader * blendFrgShader = new Shader(Shader::FRAGMENT);
blendFrgShader->setShaderSource(blendFrgShaderCode);
// Create the two texture uniforms
Uniform * tex1Sampler = new Uniform(Uniform::SAMPLER_2D, "tex1");
tex1Sampler->set(0);
Uniform * tex2Sampler = new Uniform(Uniform::SAMPLER_2D, "tex2");
tex2Sampler->set(1);
// Create the program
Program * blendProgram = new Program();
blendProgram->addShader(blendVtxShader);
blendProgram->addShader(blendFrgShader);
// The texture blending parameter will be on vertex attribute 1
blendProgram->addBindAttribLocation("vBlendParam", (GLuint) 1);
// Add everything to the StateSet
stateSet->addUniform(tex1Sampler);
stateSet->addUniform(tex2Sampler);
stateSet->setAttributeAndModes(blendProgram, StateAttribute::ON);
// Return the StateSet
return stateSet;
}
ref_ptr<StateSet> VBSPReader::readMaterialFile(std::string materialName)
{
std::string mtlFileName;
@ -745,6 +653,8 @@ ref_ptr<StateSet> VBSPReader::readMaterialFile(std::string materialName)
std::string tex2Name;
ref_ptr<Texture> texture;
ref_ptr<Texture> texture2;
ref_ptr<TexEnvCombine> combiner0;
ref_ptr<TexEnvCombine> combiner1;
ref_ptr<Material> material;
ref_ptr<BlendFunc> blend;
bool translucent;
@ -886,23 +796,80 @@ ref_ptr<StateSet> VBSPReader::readMaterialFile(std::string materialName)
// Check the shader's name
if (equalCaseInsensitive(shaderName, "WorldVertexTransition"))
{
// This shader blends between two textures based on a per-vertex
// attribute. This is used for displaced terrain surfaces in HL2 maps.
stateSet = createBlendShader(texture.get(), texture2.get());
// Make sure we have both textures
if (texture.valid() && texture2.valid())
{
// Create a StateSet for the following state
stateSet = new osg::StateSet();
// Add a material to the state set
material = new Material();
material->setAmbient(Material::FRONT_AND_BACK,
Vec4(1.0, 1.0, 1.0, 1.0) );
material->setDiffuse(Material::FRONT_AND_BACK,
Vec4(1.0, 1.0, 1.0, 1.0) );
material->setSpecular(Material::FRONT_AND_BACK,
// Attach the two textures
stateSet->setTextureAttributeAndModes(0, texture.get(),
osg::StateAttribute::ON);
stateSet->setTextureAttributeAndModes(1, texture2.get(),
osg::StateAttribute::ON);
// On the first texture unit, set up a combiner operation to
// interpolate between the textures on units 0 and 1, using
// the fragment's primary alpha color as the interpolation
// parameter (NOTE: we need ARB_texture_env_crossbar for this)
combiner0 = new osg::TexEnvCombine();
combiner0->setConstantColor(osg::Vec4f(1.0, 1.0, 1.0, 1.0));
combiner0->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE);
combiner0->setSource0_RGB(osg::TexEnvCombine::TEXTURE0);
combiner0->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
combiner0->setSource1_RGB(osg::TexEnvCombine::TEXTURE1);
combiner0->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
combiner0->setSource2_RGB(osg::TexEnvCombine::PRIMARY_COLOR);
combiner0->setOperand2_RGB(osg::TexEnvCombine::SRC_ALPHA);
combiner0->setCombine_Alpha(osg::TexEnvCombine::REPLACE);
combiner0->setSource0_Alpha(osg::TexEnvCombine::CONSTANT);
combiner0->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
combiner0->setScale_RGB(1.0);
combiner0->setScale_Alpha(1.0);
stateSet->setTextureAttributeAndModes(0, combiner0.get(),
osg::StateAttribute::ON);
// On the second texture unit, do a typical modulate operation
// between the interpolated texture color from the previous
// unit and the fragment's primary (lit) RGB color. Force the
// alpha to be 1.0, since this HL2 shader is never transparent
combiner1 = new osg::TexEnvCombine();
combiner1->setConstantColor(osg::Vec4f(1.0, 1.0, 1.0, 1.0));
combiner1->setCombine_RGB(osg::TexEnvCombine::MODULATE);
combiner1->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);
combiner1->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
combiner1->setSource1_RGB(osg::TexEnvCombine::PRIMARY_COLOR);
combiner1->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
combiner1->setCombine_Alpha(osg::TexEnvCombine::REPLACE);
combiner1->setSource0_Alpha(osg::TexEnvCombine::CONSTANT);
combiner1->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
combiner1->setScale_RGB(1.0);
combiner1->setScale_Alpha(1.0);
stateSet->setTextureAttributeAndModes(1, combiner1.get(),
osg::StateAttribute::ON);
// Add a material to the state set
material = new Material();
material->setAmbient(Material::FRONT_AND_BACK,
Vec4(1.0, 1.0, 1.0, 1.0) );
material->setDiffuse(Material::FRONT_AND_BACK,
Vec4(1.0, 1.0, 1.0, 1.0) );
material->setSpecular(Material::FRONT_AND_BACK,
Vec4(0.0, 0.0, 0.0, 1.0) );
material->setShininess(Material::FRONT_AND_BACK, 1.0);
material->setEmission(Material::FRONT_AND_BACK,
Vec4(0.0, 0.0, 0.0, 1.0) );
material->setShininess(Material::FRONT_AND_BACK, 1.0);
material->setEmission(Material::FRONT_AND_BACK,
Vec4(0.0, 0.0, 0.0, 1.0) );
material->setAlpha(Material::FRONT_AND_BACK, alpha);
stateSet->setAttributeAndModes(material.get(), StateAttribute::ON);
material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
stateSet->setAttributeAndModes(material.get(), StateAttribute::ON);
}
}
else if (equalCaseInsensitive(shaderName, "UnlitGeneric"))
{
@ -917,6 +884,9 @@ ref_ptr<StateSet> VBSPReader::readMaterialFile(std::string materialName)
{
stateSet->setTextureAttributeAndModes(0, texture.get(),
StateAttribute::ON);
stateSet->setTextureAttributeAndModes(0,
new TexEnv(TexEnv::MODULATE),
StateAttribute::ON);
// See if the material is translucent
if (translucent)
@ -965,6 +935,9 @@ ref_ptr<StateSet> VBSPReader::readMaterialFile(std::string materialName)
{
stateSet->setTextureAttributeAndModes(0, texture.get(),
StateAttribute::ON);
stateSet->setTextureAttributeAndModes(0,
new TexEnv(TexEnv::MODULATE),
StateAttribute::ON);
// See if the material is translucent
if (translucent)