Thanks, dump-generated-shaders works perfectly!
I think I know why I got confused about my code. I think my projected texture is being added first, due to being higher up the scenegraph. I assume that when the render state is created, Panda walks the hierarchy and accumulates the stages it finds and then sorts based on priority?
I was assuming my TexGen-fed stage was lower in the hierarchy but I think it’s actually stage #1 instead of stage #2, as I originally assumed. If so, the rest of my post is correct. If not, then my assumptions are wrong and so is the patch.
Found this post by David, sounds pretty concise: TextureStages/TextureAttrib stacking on node chain.
Update: After some testing, I find that yes, this is correct. The two stage orders I list below are indeed correct, as the default stage is actually rendered last because it walks the hierarchy in a reverse order. I went and modified the sort order of the stages so that the TexGenAttrib’d stage was first and last, and the shader changed the texcoord used by the world position matrix according. The only issue that I find is that it continuously generates the shader per frame? I don’t know why it’s doing this.
Anyway, here is my original post:
I’ve tried implementing a register mapping solution. A new vector list is created of texcoords that are assigned during the pass-through phase. This list is consulted when generating the in/out texcoords that are passed from vertex to fragment shader. Basically, a register mapping is added to this list if it’s merely going to be passed from the vertex shader to the fragment shader unchanged. (Which is what happens with anything that isn’t a TexGenAttrib-assigned stage.) The mapping allows for gaps in texcoord assignments.
For example, here’s how my texture stages look on my Panda node:
stage 1 -> MWorldPosition assigned with a TexMatrixAttrib (RGB colormap, ts.MModulate)
stage 2 -> Plain-Jane UV-mapped texture (RGB colormap, ts.MModulate)
It is being lit by a directional light and one ambient light. It uses no other feature.
And here is the shader which that renderstate generates (in/out order rearranged to clearly reflect what is incoming and outgoing for each stage):
//Cg
void vshader(
in float4 vtx_texcoord0 : TEXCOORD0,
in float4 vtx_normal : TEXCOORD1,
uniform float4x4 trans_model_to_world,
uniform float4x4 trans_model_to_view,
uniform float4x4 tpose_view_to_model,
uniform float4x4 mat_modelproj
float4 vtx_position : POSITION,
out float4 l_position : POSITION,
out float4 l_texcoord0 : TEXCOORD0,
out float4 l_world_position : TEXCOORD1,
out float4 l_eye_position : TEXCOORD2,
out float4 l_eye_normal : TEXCOORD3,
) {
l_position = mul(mat_modelproj, vtx_position);
l_world_position = mul(trans_model_to_world, vtx_position);
l_eye_position = mul(trans_model_to_view, vtx_position);
l_eye_normal.xyz = mul((float3x3)tpose_view_to_model, vtx_normal.xyz);
l_eye_normal.w = 0;
l_texcoord0 = vtx_texcoord0;
}
void fshader(
in float4 l_texcoord1 : TEXCOORD0,
in float4 l_world_position : TEXCOORD1,
in float4 l_eye_position : TEXCOORD2,
in float4 l_eye_normal : TEXCOORD3,
uniform sampler2D tex_0,
uniform float4x4 texmat_0,
uniform sampler2D tex_1,
uniform float4 alight_alight0,
uniform float4x4 dlight_dlight0_rel_view,
uniform float4x4 attr_material,
uniform float4 attr_color,
uniform float4 attr_colorscale
out float4 o_color : COLOR0,
) {
float4 result;
float4 l_texcoord0 = l_world_position;
l_texcoord0 = mul(texmat_0, l_texcoord0);
l_texcoord0.xyz /= l_texcoord0.w;
// Fetch all textures.
float4 tex0 = tex2D(tex_0, l_texcoord0.xy);
float4 tex1 = tex2D(tex_1, l_texcoord1.xy);
// Correct the surface normal for interpolation effects
l_eye_normal.xyz = normalize(l_eye_normal.xyz);
// Begin view-space light calculations
float ldist,lattenv,langle;
float4 lcolor,lspec,lvec,lpoint,latten,ldir,leye,lhalf;
float4 tot_ambient = float4(0,0,0,0);
float4 tot_diffuse = float4(0,0,0,0);
// Ambient Light 0
lcolor = alight_alight0;
tot_ambient += lcolor;
// Directional Light 0
lcolor = dlight_dlight0_rel_view[0];
lspec = dlight_dlight0_rel_view[1];
lvec = dlight_dlight0_rel_view[2];
lcolor *= saturate(dot(l_eye_normal.xyz, lvec.xyz));
tot_diffuse += lcolor;
// Begin view-space light summation
result = float4(0,0,0,0);
result += tot_ambient * attr_material[0];
result += tot_diffuse * attr_material[1];
result = saturate(result);
// End view-space light calculations
result *= tex0;
result *= tex1;
result *= attr_colorscale;
o_color = result * 1.000001;
}
I’m presenting a patch that includes my changes just so you can see what is going on. Note that this isn’t really what I consider commit-worthy code. (Not that I have write access to CVS, anyway!) It generates compiler warnings and is a pretty ugly hack IMHO. I think parts of the shader generator should be re-factored to keep track of what is used and where. I don’t think that just building a list of the registers in use is not enough – This is going to obfuscate the entire shader generator system, but I did it this way so I had to make the minimal changes to the current logic.
Obviously, this code is not very well tested, just with my (very narrow) test case. I haven’t tried exercising all of the render state functions and see how the output of the shader behaves.
I am presenting to help give you an idea of how I think this should be solved, by first showing the quick-fix hacky method and then maybe segueing into better methods. The concept of the shader generator isn’t complicated but there’s a lot interleaved within it.
Here’s the patch:
Index: shaderGenerator.cxx
===================================================================
RCS file: /cvsroot/panda3d/panda/src/pgraphnodes/shaderGenerator.cxx,v
retrieving revision 1.20
diff -u -r1.20 shaderGenerator.cxx
--- shaderGenerator.cxx 15 Jul 2009 15:05:52 -0000 1.20
+++ shaderGenerator.cxx 6 Aug 2009 16:54:35 -0000
@@ -571,6 +571,7 @@
pvector<char *> texcoord_vreg;
pvector<char *> texcoord_freg;
pvector<char *> tslightvec_freg;
+ pvector<int> texcoord_fmap;
char *world_position_freg = 0;
char *world_normal_freg = 0;
char *eye_position_freg = 0;
@@ -590,12 +591,17 @@
text << "void vshader(\n";
const TextureAttrib *texture = DCAST(TextureAttrib, rs->get_attrib_def(TextureAttrib::get_class_slot()));
const TexGenAttrib *tex_gen = DCAST(TexGenAttrib, rs->get_attrib_def(TexGenAttrib::get_class_slot()));
+ // Generate texcoord mapping based on what kind of inputs we need
for (int i=0; i<_num_textures; i++) {
if (!tex_gen->has_stage(texture->get_on_stage(i))) {
+ // Standard texture coords, just pass through to fshader
texcoord_vreg.push_back(alloc_vreg());
texcoord_freg.push_back(alloc_freg());
- text << "\t in float4 vtx_texcoord" << i << " : " << texcoord_vreg[i] << ",\n";
- text << "\t out float4 l_texcoord" << i << " : " << texcoord_freg[i] << ",\n";
+ // Map the assigned regs, so we can reference it later for this stage
+ texcoord_fmap.push_back(i) ;
+ int currtex = texcoord_fmap.size() - 1 ;
+ text << "\t in float4 vtx_texcoord" << currtex << " : " << texcoord_vreg[currtex] << ",\n";
+ text << "\t out float4 l_texcoord" << currtex << " : " << texcoord_freg[currtex] << ",\n";
}
}
if (_vertex_colors) {
@@ -689,7 +695,15 @@
}
for (int i=0; i<_num_textures; i++) {
if (!tex_gen->has_stage(texture->get_on_stage(i))) {
- text << "\t l_texcoord" << i << " = vtx_texcoord" << i << ";\n";
+ // look for whatever texcoord we've been mapped to for this stage
+ for (int map=0; map<texcoord_fmap.size(); map++)
+ {
+ if (texcoord_fmap[map] == i)
+ {
+ text << "\t l_texcoord" << map << " = vtx_texcoord" << map << ";\n";
+ break ;
+ }
+ }
}
}
if (_vertex_colors) {
@@ -743,7 +757,12 @@
nassertr(tex != NULL, NULL);
text << "\t uniform sampler" << texture_type_as_string(tex->get_texture_type()) << " tex_" << i << ",\n";
if (!tex_gen->has_stage(stage)) {
- text << "\t in float4 l_texcoord" << i << " : " << texcoord_freg[i] << ",\n";
+ // look for whatever texcoord we've been mapped to for this stage
+ for (int map=0; map<texcoord_fmap.size(); map++)
+ {
+ if (texcoord_fmap[map] == i)
+ text << "\t in float4 l_texcoord" << i << " : " << texcoord_freg[map] << ",\n";
+ }
}
if (tex_matrix->has_stage(stage)) {
text << "\t uniform float4x4 texmat_" << i << ",\n";
@@ -872,7 +891,8 @@
}
for (int i=0; i<_num_textures; i++) {
if (i != _map_index_height) {
- Texture *tex = texture->get_on_texture(texture->get_on_stage(i));
+ TextureStage *stage = texture->get_on_stage(i);
+ Texture *tex = texture->get_on_texture(stage);
nassertr(tex != NULL, NULL);
// Parallax mapping pushes the texture coordinates of the other textures away from the camera.
// The normal map coordinates aren't pushed (since that would give inconsistent behaviour when
@@ -880,8 +900,20 @@
if (_map_index_height >= 0 && i != _map_index_normal) {
text << "\t l_texcoord" << i << ".xyz += parallax_offset;\n";
}
+ int texcoordindex = i ; // by default, map to current texture number
+ // if we are a texgen stage then look for whatever texcoord we've been mapped to for this stage
+ if (tex_gen != NULL && tex_gen->has_stage(stage)) {
+ for (int map=0; map<texcoord_fmap.size(); map++)
+ {
+ if (texcoord_fmap[map] == i)
+ {
+ texcoordindex = map ;
+ break ;
+ }
+ }
+ }
text << "\t float4 tex" << i << " = tex" << texture_type_as_string(tex->get_texture_type());
- text << "(tex_" << i << ", l_texcoord" << i << ".";
+ text << "(tex_" << i << ", l_texcoord" << texcoordindex << ".";
switch(tex->get_texture_type()) {
case Texture::TT_cube_map:
case Texture::TT_3d_texture: text << "xyz"; break;
@@ -1270,6 +1302,8 @@
}
text << "}\n";
+ //pgraph_cat.error() << text.str() << "\n" ;
+
// Insert the shader into the shader attrib.
CPT(RenderAttrib) shattr = create_shader_attrib(text.str());
if (_subsume_alpha_test) {