Skip to main content
GameDev.net gamedev.net
🔒 Locked

Terrain: Contour Lines using pixel shader

Started by mr_hell Apr 2, 2009 at 4:20 AM 6 replies 39.3k views
Original Post
mr_hell
mr_hell
I have to draw x, y, z level lines on my relief surface. 2 soluttions: 1. Using of additional geometry (lines). Major disadvantage - i need time to calculate this lines. 2. Using pixel shader.

float4 TestP1(PS_INPUT Input): COLOR0
{
        float4 Color;
	int z = ceil(Input.Position.y);
	int y = ceil(Input.Position.z);
	int x = ceil(Input.Position.x);
	Color = Input.Color;

	if(abs(fmod(z, 100))<1.0)
	{
		Color.b = 1.0f;
		Color.r = Color.g = 0.0f;
	}
	if(abs(fmod(x, 100))<1.0)
	{
		Color.b = 1.0f;
		Color.r = Color.g = 0.0f;
	}
	if(abs(fmod(y, 100))<1.0)
	{
		Color.r = 1.0f;
		Color.b = Color.g = 0.0f;
	}

	return Color;
}



But on this way i have troubles: first far and second near Any ideas, how to get "good" level lines in shader? [Edited by - mr_hell on April 2, 2009 6:31:04 AM]
Trurl
Trurl
You can accomplish this with screen-space derivatives. Instead of drawing a band of color over a range of contours, you draw a band of color over a range of pixels near the contour value. Here's an example in GLSL, but it should port easily.

This vertex shader simply passes along the position in a varying variable.

varying vec3 k;void main(){    k = gl_Vertex.xyz;    gl_Position = ftransform();}


For reference, here's a GLSL fragment shader that does something similar to your approach. It uses smoothstep to get anti-aliased contour lines.
varying vec3 k;void main(){    vec3 f  = fract (k * 100.0);    vec3 df = fwidth(k * 100.0);    vec3 g = smoothstep(0.05, 0.10, f);    float c = g.x * g.y * g.z;    gl_FragColor = vec4(c, c, c, 1.0);}

Here's what this looks like applied to the head of the famous dragon. It has the same results that you see: lines that are too narrow in some places and too wide in others.



Now here's a fragment shader that uses screen-space derivatives to accomplish the same task. It uses smoothstep to blend between distances of 1 and 2 pixels from the desired contour value.
varying vec3 k;void main(){    vec3 f  = fract (k * 100.0);    vec3 df = fwidth(k * 100.0);    vec3 g = smoothstep(df * 1.0, df * 2.0, f);    float c = g.x * g.y * g.z;    gl_FragColor = vec4(c, c, c, 1.0);}

The result is a set of contour lines of consistent width everywhere.



knighty
knighty
Nice!

A little modification to get smoother result :)

uniform float gsize;uniform float gwidth;varying vec3 P;void main(){	vec3 f  = abs(fract (P * gsize)-0.5);	vec3 df = fwidth(P * gsize);	vec3 g = smoothstep(-gwidth*df,gwidth*df , f);	float c = g.x * g.y * g.z; 	gl_FragColor = vec4(c, c, c, 1.0);// * gl_Color;}


Edit:
another little modification 8^)
uniform float gsize;//size of the griduniform float gwidth;//grid lines'width in pixelsvarying vec3 P;void main(){	vec3 f  = abs(fract (P * gsize)-0.5);	vec3 df = fwidth(P * gsize);	float mi=max(0.0,gwidth-1.0), ma=max(1.0,gwidth);//should be uniforms	vec3 g=clamp((f-df*mi)/(df*(ma-mi)),max(0.0,1.0-gwidth),1.0);//max(0.0,1.0-gwidth) should also be sent as uniform	float c = g.x * g.y * g.z;	gl_FragColor = vec4(c, c, c, 1.0);	gl_FragColor = gl_FragColor * gl_Color;}


[Edited by - knighty on April 3, 2009 10:39:31 AM]
Trurl
Trurl
Oh yeah I totally screwed up the antialiasing on that. Not sure what I was thinking. It's still aliased on one side. We'd need two smoothsteps to make it perfect. You get the idea, at least.
mr_hell
mr_hell
Trurl
yessss, it works, thank you.
mr_hell
mr_hell
How to rewrite this shader for SM 2.0? (does not support ddx ddy operations - i have to emulate gradiants by myself).
its necessary for me!

[Edited by - mr_hell on April 3, 2009 8:28:30 AM]
rob35
rob35

How to rewrite this shader for SM 2.0? (does not support ddx ddy operations - i have to emulate gradiants by myself).
its necessary for me!

[Edited by - mr_hell on April 3, 2009 8:28:30 AM]

Yes, how do we get this to work on HLSL SM 2.0 cards? There's no ddx/ddy functions in any graphics cards but the new super-duper ones that many people don't have. It is so frustrating to think that it is impossible to have real contour lines without a SM 3.0 card! If anyone could figure out how to implement this on SM 2.0, they'd be a genius, but it seems that it is impossible unless you have the fancy ddx/ddy functions.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.