Real-time Reflection (cg shader) problem with EDT_DIRECT3D9

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
nickle
Posts: 17
Joined: Sun Feb 15, 2009 5:29 pm

Real-time Reflection (cg shader) problem with EDT_DIRECT3D9

Post by nickle »

Hi guys,

I am using this cg shader for my scene node.

Code: Select all

void main_v(float4 Position: POSITION, 
	float3 Normal: NORMAL, 
	float2 Uv: TEXCOORD0,
	float3 Tangent: TEXCOORD1,
	float3 Binormal: TEXCOORD2,

	out float4 oPosition: POSITION, 
	out float2 oUv: TEXCOORD0, 
	out float3 oLightDir: TEXCOORD1, 
	out float3 oEyeDir: TEXCOORD2,
	out float3 oHalfAngle: TEXCOORD3, 
	out float2 oUv_env: TEXCOORD4,

	uniform float4 lightPosition, 
	uniform float3 eyePosition, 
	uniform float4x4 WorldViewProj)
	
{  
	oPosition = mul(WorldViewProj, Position); 

	oUv = Uv;
	float3 LightDir = normalize(lightPosition.xyz -  (Position * lightPosition.w));
	float3 EyeDir = eyePosition - Position.xyz; 
    
	float3x3 TBN = float3x3(Tangent, Binormal, Normal); 
    
	// Transformacja do Tangent Space
	LightDir = normalize(mul(TBN, LightDir)); 
	EyeDir = normalize(mul(TBN, EyeDir)); 

	oLightDir = LightDir; 
	oEyeDir = EyeDir; 
	oHalfAngle = normalize(EyeDir + LightDir); 
	
	//float dis = -200;
	//float4 tran_Pos = float4(Position.x+Normal.x*dis, Position.y+Normal.y*dis, Position.z+Normal.z*dis, 1);
	float4 Proj_tran_pos = mul(WorldViewProj, Position); 
	oUv_env = Proj_tran_pos/Proj_tran_pos.w;
	
}

void main_f(float2 Uv: TEXCOORD0,
	float3 LightDir : TEXCOORD1,
	float3 EyeDir : TEXCOORD2,
	float3 HalfAngle : TEXCOORD3,
	float2 Uv_env: TEXCOORD4,

	uniform float bumpness,
	uniform float behind,
	uniform float specularPow,
	uniform float envPow,
	uniform float3 LightDiffuse,
	uniform float3 LightAmbient,
	uniform sampler2D DecalMap : register(s0),
	uniform sampler2D NormalMap : register(s1),
	uniform sampler2D SpecularMap : register(s2),
	uniform sampler2D EnvMap : register(s3),

	out float4 oColor : COLOR)
{

	float3 smooth = { 0.5f, 0.5f, 1.0f };
	float3 Normal = tex2D (NormalMap, Uv).rgb;
	Normal = lerp( smooth, Normal, bumpness );
	Normal = normalize (Normal*2.0 -1.0);


	float3 Diffuse = max (dot (LightDir, Normal), 0.0) * LightDiffuse;
	float3 DecalColor = tex2D (DecalMap, Uv).rgb;

	float3 AmbientColor = tex2D(DecalMap, Uv).rgb * LightAmbient;

	float3 SpecularColor = tex2D(SpecularMap, Uv).rgb;
	float Specular = max (dot (HalfAngle, Normal), 0.0);
	Specular = pow (Specular, specularPow);
	
	float2 projCoord = Uv_env;
	projCoord += float2(1.0, 1.0);
	projCoord *= 0.5;
	
	projCoord = clamp(projCoord,  0.001, 0.999);
	
	float3 EnvColor = tex2D(EnvMap, projCoord).rgb;
	
	
	if(behind == 1.0)
		envPow = 0;

	oColor = float4 (DecalColor * Diffuse + Specular * SpecularColor + AmbientColor + EnvColor*envPow, 1.0);
}

All work just fine except for the reflection part, which is a Render To Target enviornment texture.

The problem is when I get close to the reflection plane. the reflection effect got corrupted. When I move away and adjust the viewing angle of the camera. The reflection gets back to normal again. Here is a image of the effect when getting close to the reflection plane.

Image

When moving away... adjusting viewing angle facing more to the plane...

Image

I also checked out sio's PerpixelLighting_WithWater using HLSL shader, it has the same problem.

Corrupted

Image

Moving away... adjusting viewing angle, etc.

Image

However, when I switch from EDT_DIRECT3D9 to EDT_OPENGL, everything is rendered properly. There is no reflection corruption issue.

Has anyone faced a similar problem? I noticed the giroom demo from sio's website has aligning problem when moving camera. But I cannot figure out the real reason for this.

Is it related to the shader problem for calculate the envMap UV coordinates?

Guys, I really need your help on this. It is driving me mad... :(

My development env is: irrlicht 1.5, vs_2_0, ps_2_0.
My graphic card is: NVIDIA GeForce 9800 GT
[/code]
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

Ah, you need to move your perspective divide to the pixel shader (It is never supposed to be done in the vertex shader, that is just wrong!). Like so:

Code: Select all

void main_v(float4 Position: POSITION,
   float3 Normal: NORMAL,
   float2 Uv: TEXCOORD0,
   float3 Tangent: TEXCOORD1,
   float3 Binormal: TEXCOORD2,

   out float4 oPosition: POSITION,
   out float2 oUv: TEXCOORD0,
   out float3 oLightDir: TEXCOORD1,
   out float3 oEyeDir: TEXCOORD2,
   out float3 oHalfAngle: TEXCOORD3,
   out float4 oUv_env: TEXCOORD4,

   uniform float4 lightPosition,
   uniform float3 eyePosition,
   uniform float4x4 WorldViewProj)
   
{ 
   oPosition = mul(WorldViewProj, Position);

   oUv = Uv;
   float3 LightDir = normalize(lightPosition.xyz -  (Position * lightPosition.w));
   float3 EyeDir = eyePosition - Position.xyz;
   
   float3x3 TBN = float3x3(Tangent, Binormal, Normal);
   
   // Transformacja do Tangent Space
   LightDir = normalize(mul(TBN, LightDir));
   EyeDir = normalize(mul(TBN, EyeDir));

   oLightDir = LightDir;
   oEyeDir = EyeDir;
   oHalfAngle = normalize(EyeDir + LightDir);
   
   //float dis = -200;
   //float4 tran_Pos = float4(Position.x+Normal.x*dis, Position.y+Normal.y*dis, Position.z+Normal.z*dis, 1);
   float4 Proj_tran_pos = mul(WorldViewProj, Position);
   oUv_env = Proj_tran_pos;
   
}

void main_f(float2 Uv: TEXCOORD0,
   float3 LightDir : TEXCOORD1,
   float3 EyeDir : TEXCOORD2,
   float3 HalfAngle : TEXCOORD3,
   float4 Uv_env: TEXCOORD4,

   uniform float bumpness,
   uniform float behind,
   uniform float specularPow,
   uniform float envPow,
   uniform float3 LightDiffuse,
   uniform float3 LightAmbient,
   uniform sampler2D DecalMap : register(s0),
   uniform sampler2D NormalMap : register(s1),
   uniform sampler2D SpecularMap : register(s2),
   uniform sampler2D EnvMap : register(s3),

   out float4 oColor : COLOR)
{
   Uv_env.xy /= Uv_env.w;

   float3 smooth = { 0.5f, 0.5f, 1.0f };
   float3 Normal = tex2D (NormalMap, Uv).rgb;
   Normal = lerp( smooth, Normal, bumpness );
   Normal = normalize (Normal*2.0 -1.0);


   float3 Diffuse = max (dot (LightDir, Normal), 0.0) * LightDiffuse;
   float3 DecalColor = tex2D (DecalMap, Uv).rgb;

   float3 AmbientColor = tex2D(DecalMap, Uv).rgb * LightAmbient;

   float3 SpecularColor = tex2D(SpecularMap, Uv).rgb;
   float Specular = max (dot (HalfAngle, Normal), 0.0);
   Specular = pow (Specular, specularPow);
   
   float2 projCoord = Uv_env;
   projCoord += float2(1.0, 1.0);
   projCoord *= 0.5;
   
   projCoord = clamp(projCoord,  0.001, 0.999);
   
   float3 EnvColor = tex2D(EnvMap, projCoord).rgb;
   
   
   if(behind == 1.0)
      envPow = 0;

   oColor = float4 (DecalColor * Diffuse + Specular * SpecularColor + AmbientColor + EnvColor*envPow, 1.0);
}

(Did not test so there may be a compile error or two.)

UNFORTUNATELY!, when you do this you may encounter a bug in DX9 that makes nothing show up at all!

So the easy way to get around this is to do a second matrix multiplication in the fragment shader, and it works fine:

Code: Select all

void main_v(float4 Position: POSITION,
   float3 Normal: NORMAL,
   float2 Uv: TEXCOORD0,
   float3 Tangent: TEXCOORD1,
   float3 Binormal: TEXCOORD2,

   out float4 oPosition: POSITION,
   out float2 oUv: TEXCOORD0,
   out float3 oLightDir: TEXCOORD1,
   out float3 oEyeDir: TEXCOORD2,
   out float3 oHalfAngle: TEXCOORD3,
   out float4 oUv_env: TEXCOORD4,

   uniform float4 lightPosition,
   uniform float3 eyePosition,
   uniform float4x4 WorldViewProj)
   
{ 
   oPosition = mul(WorldViewProj, Position);

   oUv = Uv;
   float3 LightDir = normalize(lightPosition.xyz -  (Position * lightPosition.w));
   float3 EyeDir = eyePosition - Position.xyz;
   
   float3x3 TBN = float3x3(Tangent, Binormal, Normal);
   
   // Transformacja do Tangent Space
   LightDir = normalize(mul(TBN, LightDir));
   EyeDir = normalize(mul(TBN, EyeDir));

   oLightDir = LightDir;
   oEyeDir = EyeDir;
   oHalfAngle = normalize(EyeDir + LightDir);
   
   //float dis = -200;
   //float4 tran_Pos = float4(Position.x+Normal.x*dis, Position.y+Normal.y*dis, Position.z+Normal.z*dis, 1);
   
   oUv_env = Position;
   
}

void main_f(float2 Uv: TEXCOORD0,
   float3 LightDir : TEXCOORD1,
   float3 EyeDir : TEXCOORD2,
   float3 HalfAngle : TEXCOORD3,
   float4 Uv_env: TEXCOORD4,

   uniform float bumpness,
   uniform float behind,
   uniform float specularPow,
   uniform float envPow,
   uniform float3 LightDiffuse,
   uniform float3 LightAmbient,
   uniform float4x4 WorldViewProj,
   uniform sampler2D DecalMap : register(s0),
   uniform sampler2D NormalMap : register(s1),
   uniform sampler2D SpecularMap : register(s2),
   uniform sampler2D EnvMap : register(s3),

   out float4 oColor : COLOR)
{
   float4 Proj_tran_pos = mul(WorldViewProj, Uv_env);
   Proj_tran_pos.xy /= Proj_tran_pos .w;

   float3 smooth = { 0.5f, 0.5f, 1.0f };
   float3 Normal = tex2D (NormalMap, Uv).rgb;
   Normal = lerp( smooth, Normal, bumpness );
   Normal = normalize (Normal*2.0 -1.0);


   float3 Diffuse = max (dot (LightDir, Normal), 0.0) * LightDiffuse;
   float3 DecalColor = tex2D (DecalMap, Uv).rgb;

   float3 AmbientColor = tex2D(DecalMap, Uv).rgb * LightAmbient;

   float3 SpecularColor = tex2D(SpecularMap, Uv).rgb;
   float Specular = max (dot (HalfAngle, Normal), 0.0);
   Specular = pow (Specular, specularPow);
   
   float2 projCoord = Proj_tran_pos.xy;
   projCoord += float2(1.0, 1.0);
   projCoord *= 0.5;
   
   projCoord = clamp(projCoord,  0.001, 0.999);
   
   float3 EnvColor = tex2D(EnvMap, projCoord).rgb;
   
   
   if(behind == 1.0)
      envPow = 0;

   oColor = float4 (DecalColor * Diffuse + Specular * SpecularColor + AmbientColor + EnvColor*envPow, 1.0);
}

Now make sure to pass WorldViewProj to the pixel shader too!

Cheers, now make sure to read up on "Perspective Divide" and why it should be done per-pixel!
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
zillion42
Posts: 324
Joined: Wed Aug 29, 2007 12:32 am
Location: Hamburg, Germany

Post by zillion42 »

damn, Blindside who teaches you that kind of stuff.... 8)

And yes, I can confirm the realistic water & reflection scene node have the same problem... I just ignored it though since I'm never really that close to the surface when using a character controller. Nice to hear it can be fixed...
nickle
Posts: 17
Joined: Sun Feb 15, 2009 5:29 pm

Post by nickle »

Thanks BlindSide & zillion42 ... It is indeed the vertex shader caused the corription... Moving calculation of UV to frag shader just solved the problem... Thanks for the tip...
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: Real-time Reflection (cg shader) problem with EDT_DIRECT

Post by robmar »

Just found this post, had the same up-close distortions in the HLSL version of a similar reflection layer that I´ve been unable to explain over the last year! :roll:

Does this problem also occur with the CG shader in Irrlicht too with DX driver in use?

Adding the suggested fix to my similar shader, the reflection cords get "lost in space" for some reason.

My shader just creates a alpha transparent layer where the material is the reflection texture (up camera method), and the water effect is created by offsetting the position using sin etc.

Here is the shader:

Code: Select all

// HLSL Reflection Water Shader
 
uniform float Time;
float addition;
uniform float4x4 mWorldViewProj;
float WaveHeight, WaveLength, WaveSpeed;
float4 AddedColor, MultiColor;
float UnderWater, WaveDisplacement, WaveRepetition, RefractionFactor;
 
struct VS_OUTPUT
{
    float4 Position : POSITION;
    float4 Diffuse : COLOR0;
    float2 TexCoord : TEXCOORD0;
    float4 pos : TEXCOORD1;
};
 
VS_OUTPUT mainVS( in float4 vPosition : POSITION,
                      in float3 vNormal : NORMAL,
                      float2 texCoord : TEXCOORD0 )
{
    VS_OUTPUT Output;
 
Output.pos = vPosition;
 
    Output.Position = mul(vPosition, mWorldViewProj);
 
    addition = (sin((vPosition.x/WaveLength) + (Time * WaveSpeed / 10000.0))) +
                     (cos((vPosition.z/WaveLength) + (Time * WaveSpeed / 
 
10000.0)));
//    Output.Position.y += addition * WaveHeight;
    Output.Diffuse = float4(addition, addition, addition, addition);
    Output.TexCoord = Output.Position / Output.Position.w;
    return Output;
}
 
struct PS_OUTPUT
{
    float4 RGBColor : COLOR0;
};
 
texture ReflectionTexture;
 
sampler2D MySampler = sampler_state
{
    Texture = <ReflectionTexture>;
    AddressU = CLAMP;
    AddressV = CLAMP;
};
 
PS_OUTPUT mainPS( float2 TexCoord : TEXCOORD0,
                     float4 Position : POSITION,
                     float4 Diffuse : COLOR0,
             float2 pos : TEXCOORD1)
{
    PS_OUTPUT Output;
 
Position = mul(pos, mWorldViewProj);
float2 projCoord = Position / Position.w;   // Blindside´s bug fix
   projCoord += float2(1.0, 1.0);
   projCoord *= 0.5;
   
projCoord.y += addition * WaveHeight;
 
   projCoord = clamp(projCoord,  0.001, 0.999);
 
//    float2 projCoord = TexCoord;
    float addition = Diffuse.r;
 
//    projCoord += float2(1.0, 1.0);
//    projCoord *= 0.5;
    projCoord.x += sin(addition * WaveRepetition) * (WaveDisplacement / 
 
1000.0);
    projCoord.y += cos(addition *WaveRepetition) * (WaveDisplacement / 1000.0);
    if(UnderWater == 1.0)
        projCoord.y = 1.0 - projCoord.y;
    projCoord = clamp(projCoord, 0.001, 0.999);
    float4 refTex = tex2D(MySampler, projCoord);
    refTex = (refTex + AddedColor) * MultiColor;
    Output.RGBColor = refTex;
    if(UnderWater == 1.0)
        Output.RGBColor *= (MultiColor / 1.1);
    Output.RGBColor.a = RefractionFactor;
    return Output;
}
 
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: Real-time Reflection (cg shader) problem with EDT_DIRECT

Post by robmar »

Blindside´s fix doesn´t seem to work...

I rewrote DeusXL / SIO2 ´s GLSL shader in HLSL but the same problem!

Help!

Here is the uopdated shader:

Code: Select all

// HLSL Reflection Water Shader - original
 
// Problem: Distortion occuring in reflection when camera is close to reflection surface
 
float4x4 mWorldViewProj;
uniform float Time;
uniform float WaveHeight, WaveLength, WaveSpeed;
float4 waterpos;
float4 addition;
float4 AddedColor, MultiColor;
float UnderWater, WaveDisplacement, WaveRepetition, RefractionFactor;
 
struct VS_INPUT
{
    float4 position : POSITION;
    float2 texCoord : TEXCOORD0;
    float3 vNormal : NORMAL;    
};
 
struct VS_OUTPUT
{
    float4 position : POSITION;
    float2 TexCoord : TEXCOORD0;
    float4 Uv_env: TEXCOORD1;
};
 
VS_OUTPUT mainVS( VS_INPUT In )
{
    VS_OUTPUT Output;
 
    Output.Uv_env = In.position;
 
    waterpos = mul(In.position, mWorldViewProj);
 
    Output.position = waterpos;
 
    addition = (sin((In.position.x/WaveLength) + (Time * WaveSpeed / 10000.0))) +
                     (cos((In.position.z/WaveLength) + (Time * WaveSpeed / 10000.0)));
 
    waterpos.y += addition * WaveHeight;
 
    Output.TexCoord = waterpos / waterpos.w;
 
    return Output;
}
 
struct PS_OUTPUT
{
    float4 RGBColor : COLOR0;
};
 
texture ReflectionTexture;
 
sampler MySampler = sampler_state
{
    Texture = <ReflectionTexture>;
    AddressU = CLAMP;
    AddressV = CLAMP;
};
 
PS_OUTPUT mainPS( VS_OUTPUT In )
{
    PS_OUTPUT Output;
 
    float2 projCoord = In.TexCoord; // Works but with distortions when close to surface
 
/*
    float4 Proj_tran_pos = mul(In.Uv_env, mWorldViewProj);   // Bad reflection coords
    float2 projCoord = Proj_tran_pos.xy / Proj_tran_pos.w;
*/
 
//    float2 projCoord = waterpos / waterpos.w; // Bad reflection coords
 
    projCoord += float2(1.0,1.0);
    projCoord *= 0.5;
 
    projCoord = clamp(projCoord, 0.001, 0.999);
 
    projCoord.x += sin(addition * WaveRepetition) * (WaveDisplacement / 1000.0);
    projCoord.y += cos(addition * WaveRepetition) * (WaveDisplacement / 1000.0);
 
/*
    float4 projCoord = waterpos / waterpos.w;
    projCoord += float4(1.0,1.0,1.0,1.0);
    projCoord *= 0.5;
    projCoord.x += sin(addition * WaveRepetition) * (WaveDisplacement / 1000.0);
    projCoord.y += cos(addition * WaveRepetition) * (WaveDisplacement / 1000.0);
*/
    projCoord = clamp(projCoord, 0.001, 0.999);
 
    if(UnderWater == 1.0)
      projCoord.y = 1.0 - projCoord.y;
 
    float4 refTex = tex2D(MySampler, projCoord.xy);
 
    refTex = (refTex + AddedColor) * MultiColor;
    Output.RGBColor = refTex;
    if(UnderWater == 1.0)
        Output.RGBColor *= (MultiColor / 1.1);
    Output.RGBColor.a = RefractionFactor;
    return Output;
}
 
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: Real-time Reflection (cg shader) problem with EDT_DIRECT

Post by robmar »

This distortion on the X plane causing jig-jag reflections only occurs when the camera is up very close to the reflection surface or near parallel, so could it be related to DirectX float precision, as I´m using the fast maths settings?
Post Reply