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;
}
Passing parameters between shaders?
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:
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]
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.
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.
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).
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)?
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
Fragment program
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:
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:
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement