Nvidia's CG bug? + another question

Started by
10 comments, last by Dolf 15 years, 10 months ago
Hey guys, today I encountered something extremely strange. This is a hand-made phong model + normal mapping. I am fully ware of the "lit" function. For the normal mapping to work, I have to declare the float4 color; at the 2nd place in the code, at the 1st place it wont work.

struct input
{
	float2 uv       : TEXCOORD0;
	float3 normal   : TEXCOORD1;
	float4 pos		: TEXCOORD2;
	float3 T :TEXCOORD3;
	float3 B :TEXCOORD4;
};
struct output 
{
	float4 color : COLOR;
};
output pixelfunction( 
					 input IN, uniform sampler2D decal : TEX0,
					 uniform sampler2D normals : TEX1,
					 uniform float3 light,
					 uniform float3 eye,
					 uniform float3 spotdirection
					 )
{
	output OUT;
	//float4 color = tex2D( decal, IN.uv ); // wtf, it doesnt work with this one
	
	float3 lightToPos = normalize( light - IN.pos );
	spotdirection = normalize( spotdirection );
	float angle = saturate( dot( lightToPos, spotdirection ) ); 

	float dist = distance(IN.pos, light);
	float att = 2.0f / (dist*dist);
	
	float3 T = normalize(IN.T);
	float3 B = normalize(IN.B);
	float3 N = normalize(IN.normal);

	float3 NMN = (2*tex2D( normals, IN.uv )) - float3(1,1,1); //key point, if i put the float4 color beneath this it works
	float3 PN = N*NMN.z + T*NMN.x + B*NMN.y ;

	float3 L = normalize( light - IN.pos );
	float3 V = normalize( eye - IN.pos );
	float3 H = normalize( L + V );

	//float4 color = tex2D( decal, IN.uv ); //omg, with this one it works
	float4 diffuse = att*color*dot( PN, L );
	float4 specular = att*color * pow( max( dot( PN,H),0), 15);

	specular.g = 0;
	specular.b = 0;

	OUT.color = (diffuse + specular);

	return OUT;
}




I have to declare my " float4 color; " below the line where i say " //key point " Why is that? my 2nd question: Is there any way of storing all your vertex data on the gpu so you can do your keyframe interpolation on the GPU(without sending all the vertices every render)? and if so: how? (conceptually), because how could a vertex know where to interpolate to if you don't pass the new vertex position?(i dont wanna use immediate mode and pass it as a texturecoordinate, i wanna have all the geometry data stored on the gpu) [Edited by - Dolf on May 30, 2008 8:08:18 AM]
Advertisement
Or should I have posted this in the directX section?
1) I'm assuming that by "it wont work" you mean the Cg program will not compile. What kind of error string are you getting with the failed compilation?

In case you aren't sure how to get the error, (or for the reference of whoever else is reading this thread), you can register an error callback method with Cg that I have found quite helpful in these situations.

For example, this is how I do it:

bool ShaderFactory::Initialize(){     ...     // create the context     sCgContext = cgCreateContext();     if( !sCgContext )     {         Log::LogError( "Failed to create Cg context!" );         return false;     }     // get a suitable vertex profile     sCgVertexProfile = cgGLGetLatestProfile( CG_GL_VERTEX );     if( sCgVertexProfile == CG_PROFILE_UNKNOWN )     {	         Log::LogError( "Failed to query valid vertex profile!" );         return false;     }     // get a suitable fragment profile     sCgFragmentProfile  = cgGLGetLatestProfile(CG_GL_FRAGMENT);     if( sCgFragmentProfile == CG_PROFILE_UNKNOWN )     {         Log::LogError( "Failed to query valid fragment profile!" );         return false;     }     // set callback     cgSetErrorCallback( CGErrorHandler );     ...     return true; }


and CGErrorHandler is my callback defined as:

#define BREAK_ON_SHADER_ERROR { do { __asm int 3 } while( GetFalseValue())static void CGErrorHandler(){   CGerror err = cgGetError();   const char * errStr         = cgGetErrorString(err);   const char * errListing     = cgGetLastListing( sCgContext );   Log::LogError( "Cg Error!" );   Log::LogError( errStr );   Log::LogError( errListing );   BREAK_ON_SHADER_ERROR;}  


cgGetErrorString() will give you a fairly vague but sometimes useful error message. cgGetLastListing() will give you the exact compile error reported from cgc. The is usually the most helpful message. BREAK_ON_SHADER_ERROR is just a utility for forcing VisualStudio to insert a breakpoint if the method is called.

2) I don't believe that you can explicitly get access to more than one vertex position attribute within a Cg program. So, it would seem that in order to interpolate between two keyframe positions you are going to have to pack the additional vertex position in somewhere...possibly a texture coordinate unit. Of course this does not have to be done in immediate mode, but sometimes using the gpu to offload cpu workload requires some trickery.
I dont get a compile error, it compiles perfectly fine but the area i was rendering turned black.

"Of course this does not have to be done in immediate mode"

How do you do it without using immediate mode? I can't store more than a few keyframes in UV coordinates.
I can do it in immediate mode using the texture coordinates.
1) I'm not sure if this is the problem, but in my experience a black render is either an improperly bound texture or improperly bound texture coordinates. Have you tried boiling the shader down any? For example, first just return the sampled decal map and assure that works. Then, slowly build the shader up to find the breaking point?

2) My mistake, I thought you meant to cache the current and next keyframe on the gpu for interpolation, not the entire animation sequence. I'm not sure that such a large amount of data could be cached (or if you would have any guarantees that it would stay cached during the rendering of other materials.) Also, by doing it in immediate mode you could only cache at most the number of texturing units the hardware supported. If you get this to work and find some big performance wins, I'd love to hear about it.
1: My normal mapping works when I have that line at position 2 (where i have the comment of something like //here it works). But at the first position, it doesnt work.
There "should" be no difference between the 2 versions. When I retrieve the color at the first position, I don't do anything with it. Or with the UV coordinates. But it doesn't work.

Maybe I should upload the entire project so you guys can compile it yourselves and see what the problem is? I only tested this on my own laptop and someone else's laptop, which happens to be the exact same type of laptop. So that didn't quite prove anything.

2: Yeah, I heard about GPU-only keyframe interpolation instead of using immediate mode, then when I asked how it was done I didn't get an answer I was looking for.
Just curious, have you tried this FX file in FX Composer? Have you tried to debug it in PIX?

Also, can you post the compiled machine code of the two versions?
I do real things with imaginary numbers
3 questions: what is your hardware, OS and Cg version?

In the past I have had all sorts of weird Cg issues where changing the order of operations should not make a difference but made the shader work or not work... what I don't generally understand is that this was with nVidia hardware and the equivalent GLSL shader would work just fine (which is really odd since I have read that internally nVidia drivers translate GLSL into Cg and then compile)... at any rate one thing you may wish to try is to explicitly state what components you are taking, this probably won't make a difference, but you never know, so for example change:

float3 NMN = (2*tex2D( normals, IN.uv )) - float3(1,1,1); 


to

float3 NMN = (2*tex2D( normals, IN.uv ).rgb) - float3(1,1,1); 


it should not matter, but you never know... another thing you can do is use the command line Cg compiler, cgc and examine its error/warning messages along with
it's assembly output.

for your second question, one of the easiest ways to do keyframe interpolation on GPU is to do (in your vertex shader):

uniform float interpol_percent;struct input{	float2 uv       : TEXCOORD0;	float3 normal   : TEXCOORD1;	float4 pos1		: TEXCOORD2;        float4 pos2 : TEXCOORD5;	float3 T :TEXCOORD3;	float3 B :TEXCOORD4;};


and then do in your Cg- equivalent of main, add:

OUT.position=modelViewMatrix*( IN.pos1*(1.0-interpol_percent) + IN.pos2*interpol_percent);


and whatever you are also sending to your fragment shader.

I have quasi memory there is a function called mix to do just the above, but I am not sure.

Then in your code set pos1 to be associated to the vertex data of the frame you are interpolating from and pos2 to be associated to the vertex data you are interpolating too.
Close this Gamedev account, I have outgrown Gamedev.
I am trying to talk about keyframe interpolation completely on the gpu. So you don't have any data traffic from your CPU to GPU. Is this possible?

The way you discribe can only be implemented in immediate mode?

Aobut the sader, i just tried with .rgb, and it gives me the same result as without .rgb (which doesnt work in position 1, but does work for position 2)

So I guess its just a bug in CG then
In OpenGL, one uses VBO (Vertex Buffer Objects), these are just chunks of data stored on VRAM of the video card. When it is time to render you bind the VBO's and tell OpenGL to use that data as the inputs to your shader...for DirectX, I don't know the function names, but go to the DirectX forum and people will be happy to tell you what they are and how to do it... there are also tutorial on line showing, some googling will probably get you eventually what you need (also the Cg SDK you can get (for free) from nVidia has lots of samples in it, those samples will show you how too.

Close this Gamedev account, I have outgrown Gamedev.

This topic is closed to new replies.

Advertisement