World Space View Direction in surface shader

Hi.

How to obtain World Space View Direction vector in vertex+fragment shader (not in surface shader!) exactly in the way that the surface shader provides?

In a surface shader it’s simply done by:

struct Input {
   //blahblah
   float3 viewDir;
   //blahblah
};

void surf (Input IN, inout SurfaceOutput o) {
   //need this IN.viewDir vector!!!
   o.Emission = normalize(IN.viewDir);
}

but in my vertex+fragment shader

//blahblahblah

v2f vert(appdata_base v){
   //blahblah

   //doesn't help
   //o.viewDir = WorldSpaceViewDir(v.vertex);
   //and this
   //o.viewDir = ObjSpaceViewDir(v.vertex);
   o.viewDir = ???

   //blahblah
}

how that would be done to obtain the same vector?

WorldSpaceViewDir(v.vertex) should do it in your vertex shader.

But the code for that function (taken straight from UnityCG.cginc) is;

// Computes world space view direction
inline float3 WorldSpaceViewDir( in float4 v )
{
	return _WorldSpaceCameraPos.xyz - mul(_Object2World, v).xyz;
}

What is it you’re trying to achieve?

2 Likes

Farfarer, then why viewDir in surface shader:

Shader "Temp/ViewDirSurface" {
	SubShader {
		CGPROGRAM
		#pragma surface surf Lambert

		struct Input {
			float3 viewDir;
		};

		void surf (Input IN, inout SurfaceOutput o) {
			o.Emission = IN.viewDir;
			o.Alpha = 1;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}

and vertex + fragment:

Shader "Temp/ViewDirVertexFragment" {
	SubShader {
		Pass {
			Blend SrcAlpha OneMinusSrcAlpha
			
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"
			
			struct v2f{
				float4 pos : POSITION;
				float3 viewDir : TEXCOORD0;
			};
			
			v2f vert(appdata_base v){
				v2f o;
				o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
				o.viewDir = WorldSpaceViewDir(v.vertex);
				return o;
			}
			
			float4 frag(v2f i) : COLOR{
				return float4(i.viewDir, 1);
			}
			ENDCG
		}
	} 
	FallBack "Diffuse"
}

gives me different results? What i’m doing wrong?

I’m trying to achieve exactly the same vector like struct Input {float3 viewDir;}; returns for surface shader, but in my vertex+fragment program.

I have no idea.

I’ve copy/pasted those examples you posted into two new shaders and stuck each one onto an identical sphere… they both give me identical outputs.

Wow… Can you post screenshots, please?
I’m using Unity 3.2… Maybe that’s the reason?

Oh… Unity in surface shader returns different viewDir for Forward and for Deferred lighting.

Farfarer, turn on deferred lighting and look at the sphere, which uses surface shader.
I need to achieve this result in vertex+fragment shader.

Found the solution (using #pragma debug in surface shader and looking at the compiled shader source). If anybody interested:

The trick is to use UnityCG.cginc directive TANGENT_SPACE_ROTATION; in v2f vert(appdata_tan v) vertex program, which gives actually:
float3 binormal = cross( v.normal, v.tangent.xyz ) * v.tangent.w;
float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal );
and then by o.viewDir = mul (rotation, ObjSpaceViewDir(v.vertex)); i can achieve vector that i can use in fragment program for dot(viewDir,normal) where normal = unpacknormal(blahblah)…

Works with deferred forward rendering paths. Gives the same result.

3 Likes

I tried multiplying the vertex’s normal with different combinations of the built in unity matrixes for model, view, projection etc. but none of them were quite right. This might be the solution i’m looking for. Thanks!