• Advertisement
Sign in to follow this  

Passing parameters between shaders?

This topic is 3317 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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]

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites
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)?

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement