Passing parameters between shaders?

Started by
3 comments, last by JackTheRapper 15 years, 2 months ago
Hi, I'm currently learning cg with OGL 1.5 for a terrain project I will be starting. I've got a basic splatting fragment shader that blends a grass texture onto a dirt texture, along with blending in the vertex colours and an extra hi-res detail texture. Now, to determine whether to use the hi res texture or not, I'm passing the cam position to the fragment shader and testing the fragment's vertex position against the camera position, a la:

struct CompositeLitSplat
{
	// Output fragment colour
	float4 color : COLOR;

};

/********************************************
*											*
*	Hi-Res Splatting Over 2 Texture Units	*
*	-------------------------------------	*
*											*
*	Performs splatting using 2 textures		*
*	over 1 texure unit. A 2nd texture unit	*
*	is used to add hi-res detail when the	*
*	tile distance is close enough to		*
*	warrant it (calculated from cam and v	*
*	pos dist). The hi-res texture contains	*
*	2 textures, one in the red channel and	*
*	one in the green.						*
*											*
*	Also calculates a simple per-vertex		*
*	ambient + diffuse lighting based on the *
*	supplied light position (x,y).			*
*											*
********************************************/


CompositeLitSplat main(float4			 inColour		: COLOR0,
					   float2			 texCoord		: TEXCOORD0,	// Tex stage 1 coordinates (used for both textures)
					   float2			 lightPos		: TEXCOORD1,	// Tex stage 2 coordinates (light pos x,y)
					   float			 blendAmount	: TEXCOORD2,	// Tex stage 3 coordinates (detail interpolationn amount)
					   uniform sampler2D splatTextures	: TEX0,			// Tex stage 1 lo-res base + detail (dirt + grass),
					   uniform sampler2D hiresTexture   : TEX1,			// Tex stage 2 hi-res detail (green = grass, red = dirt)
					   uniform float3	 vPos			: POSITION,		// Vertex position
					   uniform float1	 Ambient,						// Ambient lighting level			
					   uniform bool		 Lighting,						// Toggle lighting on/off
					   uniform float3 	 CamPos)						// Camera position (used to determine whether to use hi res or not)
{
	CompositeLitSplat OUT;

	// We need to the dirt and grass tex coords from the base coords as they are on the same texture
	float2 dirtCoord, grassCoord;

	// Used to hold grass + hi res result
	float4 grass, dirt;

	// Amount of hi-res detail texture blended onto grass texture
	const float hiresAmount = 0.07;

	OUT.color = inColour;

	// Dirt (1st half)
	dirtCoord.x = texCoord.x * 0.5;
	dirtCoord.y = texCoord.y;

	// Grass (2nd half)
	grassCoord.x = (texCoord.x * 0.5) + 0.5;
	grassCoord.y = texCoord.y;
	
	// Are we close enough to use the hi-res texture?
	if(distance(CamPos, vPos) < 25)
	{
		// Yes. Add the high-detail texture in the 2nd texture unit to the base texture from the appropriate colour channel
		grass = ((1 - hiresAmount) * tex2D(splatTextures, grassCoord)) + (hiresAmount * tex2D(hiresTexture, texCoord).g);
		dirt = ((1 - hiresAmount) * tex2D(splatTextures, dirtCoord)) + (hiresAmount * tex2D(hiresTexture, texCoord).r);
	}
	else
	{
		// No. Only use the lo res textures
		grass = tex2D(splatTextures, grassCoord);
		dirt = tex2D(splatTextures, dirtCoord);
	}

	// Lighting calculation result
	float d;

	// We using lighting?
	if(Lighting == false)
	{
		// No, setting d to 1 will result in straight up texture splatting
		d = 1.0;
	}
	else
	{  
		// Calculate the simple diffuse lighting equation
		d = clamp(1.0 - pow(dot(lightPos, lightPos), 0.5), 0.0, 1.0) + Ambient;
	}

	// Do an additive blend of the base and detail (blend 0 = 100% base, blend 1 = 100% grass, blend 0.5 = 50/50)
	OUT.color *= ((1 - blendAmount) * grass) + (blendAmount * dirt);

	
	// Now modulate the splat by the lighting result
	OUT.color *= d;

	return OUT;
}

I have 2 questions: am I doing this correctly by using the vPos : POSITION uniform value? It works, but I can't figure out what this position is (vertex? if so, which one? Am I doing it wrong? etc). My next question is, if the above is correct, is there any way I can calculate the distance in the vertex shader and pass a boolean to the fragment shader to avoid having to calculate the distance for each fragment? Thanks [Edited by - JackTheRapper on January 22, 2009 9:53:55 AM]
Advertisement
I'd say the UNIFORM keyword is wrong, since uniforms are parameters set externally. What you want is the varying fragment position, so remove the UNIFORM.

You can calculate the distance in the vertex shader and put the result into a vertex attribute. I'm not sure which attributes are passed to the fragment shader, but texture coordinates always should, so just use a texture coordinate set that's not in use (e.g. if you have 8 texture coordinate sets available, use number 8).

Those attributes are interpolated across the primitives, so you should get a fairly accurate distance in the fragment shader.
If I was helpful, feel free to rate me up ;)If I wasn't and you feel to rate me down, please let me know why!
vPos is the untransformed vertex position that you passed to OpenGL, either via the old fashioned (but still useful for debugging code) immediate mode glVertex, or the preferable VBO functions. This means vPos is in local coordinates, and if you are performing transformation on the object you are drawing, you need to either transform the camera position to the object space (localisation I usually call this), or transform the vertex position to the camera space (globalisation I usually call this, assuming the camera position is in global coordinated).
I would go with transforming the camera position into object local space before passing it to the shader, then you can compare directly against the vPos without transforming it first. Otherwise you would need to transform vPos twice in the shader (once to get it to camera local space, once to get it to homogonised coordinate space via the view-projection matrix).
Also source tags are your friend, they make your post more readable (see the forum faq).
Oops, my bad! post fixed :D

Excellent, thanks for the information. The vertices for the (eventual) heightmap are in world space so as far as that goes, it's all good.

I removed the uniform qualifier and now the shader won't compile. Are you sure that position can be passed as a varying input to a frament program?

EDIT: sorry, didn't see both replies

Lord_Evil: yeah, I kinda figured that would have to be the case. One question about using things like tex coords for data: if a card only supports, say, 4 texture units, is it illegal to specify a set of coords for, say, a 5th texture stage (even though u aren't technically using that stage)?
Solved it :D Didn't realise that writing a vert shader means all transformation is offloaded to the developer. works like a charm now. here's the listing for what it's worth, a simple prototype of a lo-res terrain + hi-res detail splatter:

Vertex program
void main(in float4  vertPosition			: POSITION,			// Vertex position		  in float4  vertColour				: COLOR0,			// Vertex colour		  in float2  texCoord				: TEXCOORD0,		// Tex stage 1 coordinates (used for both textures)		  in float2	 lightPos				: TEXCOORD1,		// Tex stage 2 coordinates (light pos x,y)		  in float	 blendAmount			: TEXCOORD2,		// Tex stage 3 coordinates (detail interpolationn amount)		  out float4 oPosition				: POSITION,			// Translated and projected vertex position          out float3 oFragPosition			: TEXCOORD3,		// Translated, projected and interpolated vertex position per fragment		  out float4 oColour				: COLOR,				  out float2 oTexCoord				: TEXCOORD0,			  out float2 oLightPos				: TEXCOORD1,			  out float	 oBlendAmount			: TEXCOORD2,			  uniform float4x4 ModelViewProj	: state.matrix.mvp)	// ModelViewPorojection matrix{	// Translate and project the vertex	oPosition =  mul(ModelViewProj, vertPosition);	// Now copy the supplied arguements over for use in the fragment shader	oFragPosition = oPosition;	oColour		  = vertColour;	oTexCoord	  = texCoord;	oLightPos	  = lightPos;	oBlendAmount  = blendAmount;}


Fragment program
void main(in float4			   fragColour		: COLOR0,		// Fragment colour (interpolated from vertex colour)		  in float2			   texCoord			: TEXCOORD0,	// Tex stage 1 coordinates (used for both textures)		  in float			   blendAmount		: TEXCOORD2,	// Tex stage 3 coordinates (detail interpolationn amount)		  in float3			   fragPos			: TEXCOORD3,	// Frag position interpolated from vertex position		  in uniform sampler2D splatTextures	: TEX0,			// Tex stage 1 lo-res base + detail (dirt + grass),		  in uniform sampler2D hiresTexture		: TEX1,			// Tex stage 2 hi-res detail (green = grass, red = dirt)		  in uniform bool	   useHiRes,		  out float4		   oColour			: COLOR){	// Cam is at origin as vert/frag pos is in projected world space	float3 CamPos = float3(0.0,0.0,0.0);	// Cut-off distance for the high res detail texture	float maxDist = 15.0;	// Calculate the frag's distance from the viewer	float fragDist = distance(CamPos, fragPos);	// We need to the dirt and grass tex coords from the base coords as they are on the same texture	float2 dirtCoord, grassCoord;	// Used to hold grass + hi res result	float4 grass, dirt;	// Grab the colour from the vertex stage (can be used to modulate the texture colour)	oColour = fragColour;	// Dirt (1st half)	dirtCoord.x = texCoord.x * 0.5;	dirtCoord.y = texCoord.y;	// Grass (2nd half)	grassCoord.x = (texCoord.x * 0.5) + 0.5;	grassCoord.y = texCoord.y;		// Are we close enough to use the hi-res texture?	if((useHiRes == true) && (fragDist < maxDist))	{		// Amount of hi-res detail texture blended onto grass texture		float hiresAmount;		// Max distance = 15. Therefore, any frags within 0 to 15 units is hi-res. However, in order to stop the		// hi-res texture "popping" into place, we will blend the hi res amount over the last 2 thirds of the distance		// range. The further away the frag, the less hi res detail blended in. This gives us a smooth transition		// as we zoom in and out, making the hi-res "popping" non-existent		if(fragDist > 5.0)		{			hiresAmount = 0.07 * (((maxDist - fragDist) / maxDist) + 0.5);		}		else		{			hiresAmount = 0.07;		}		// Yes. Add the high-detail texture in the 2nd texture unit to the base texture from the appropriate colour channel		grass = ((1 - hiresAmount) * tex2D(splatTextures, grassCoord)) + (hiresAmount * tex2D(hiresTexture, texCoord).g);		dirt = ((1 - hiresAmount) * tex2D(splatTextures, dirtCoord)) + (hiresAmount * tex2D(hiresTexture, texCoord).r);	}	else	{		// No. Only use the lo res textures		grass = tex2D(splatTextures, grassCoord);		dirt = tex2D(splatTextures, dirtCoord);	}	// Do an additive blend of the base and detail (blend 0 = 100% base, blend 1 = 100% grass, blend 0.5 = 50/50)	oColour *= ((1 - blendAmount) * grass) + (blendAmount * dirt);}


I tried calculating the distance in the vertex shader and then passing it on to the fragment shader to be interpolated per frag, but with such a basic prototype (4 quads splatted) it was too early to say if there was any real impact from doing so

screen shot:
Photobucket

This topic is closed to new replies.

Advertisement