- Home /
Get local position in surface shader
Is there any way to get the local position (relative to the mesh) on the surface shader?
This is part of my code:
fixed4 _Color;
fixed4 _ReflectColor;
half _Shininess;
fixed _DrawLimit;
struct Input {
float2 uv_MainTex;
float3 worldRefl;
float3 worldPos;
};
void surf (Input IN, inout SurfaceOutput o) {
clip(IN.worldPos.z - _DrawLimit);
fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
fixed4 c = tex * _Color;
o.Albedo = c.rgb;
o.Gloss = tex.a;
o.Specular = _Shininess;
fixed4 reflcol = texCUBE (_Cube, IN.worldRefl);
reflcol *= tex.a;
o.Emission = reflcol.rgb * _ReflectColor.rgb;
o.Alpha = reflcol.a * _ReflectColor.a;
}
Instead of using worldPos
I need the local position. I can't find a way to get it in the docs (at least here).
Answer by tanoshimi · Oct 23, 2013 at 07:12 PM
Untested, but here's an idea: First, add a member to your Input structure:
struct Input {
float2 uv_MainTex;
float3 localPos;
};
Then add a vertex modifier function that populates the localPos member with the vertex information from appdata:
#pragma surface surf Lambert vertex:vert
void vert (inout appdata_full v, out Input o) {
UNITY_INITIALIZE_OUTPUT(Input,o);
o.localPos = v.vertex.xyz;
}
Now in your surf function you should be able to access IN.localPos as:
void surf (Input IN, inout SurfaceOutput o) {
// Do something with IN.localPos....
}
It works well ! BUT, what about objects with different axis. It's always the same problem with axis in different applications (3DS$$anonymous$$ax, Unity, $$anonymous$$aya, Blender, etc...) wen you import them. Is there a way to correct that in the shader ?
Answer by Arwahanoth · Nov 23, 2016 at 06:25 PM
In fact I just discovered that we don't need the "vert" and we could do it only in "surf" with:
float3 localPos = IN.worldPos - mul(unity_ObjectToWorld, float4(0,0,0,1)).xyz;
Don't forget to declare worldPos in Input:
struct Input {
/// [....]
float3 worldPos;
};
Thanks for this addition! It kicked ass for me. Fixed the weird model import "what is y/up?" problem above. :)
Can you explain me what you are doing in the first line code
He's converting the origin of the object, which is by definition (0, 0, 0) in its local space, into world space. Then he's subtracting that from IN.worldPos (which is the interpolated world position of the fragment) in order to get the difference between the fragment and the origin of the object. The difference between the fragment's position and the object's origin = its local position.
This is sometimes better than just getting the local position directly from the vertex shader input (via the POSITION semantic) because imported 3d models are often rotated, so the object's local axes won't be aligned with the world space axes.
Can you clearly explain to me how mul(unity_ObjectToWorld, float4(0,0,0,1)).xyz works?
This does only work in very special cases. Your object must not be scaled and must not be rotated in any way. Otherwise your result is completely wrong. Also using a matrix multiplication to get the last column of a matrix is just a waste of GPU power.
float3 pos = unity_ObjectToWorld._m03_m13_m23;
This will get the x,y and z of the last column. The best solution is to just pass the local space position along in the vertex shader. Note that a surface shader will still compile a vertex and a fragment shader anyways. It's just a fancy highlevel way to write shaders. If you really need to convert the worldspace position back to local space, use the unity_WorldToObject
matrix ins$$anonymous$$d
float3 localPos = mul(unity_WorldToObject, IN.worldPos);
$$anonymous$$eep in $$anonymous$$d that worldPos need to be a float4
, not a float3. We're dealing with homogeneous coordinates here. Alternatively you can use something like
float3 localPos = mul(unity_WorldToObject, float4(IN.worldPos, 1));
Long time not used Unity but if I take you're advice the code should be:
float3 localPos = mul(unity_WorldToObject, IN.worldPos);
struct Input {
/// [....]
float4 worldPos;
};
Right?
thank you very much! I was having problems because of the scale and this helped me fix them
Your answer
Follow this Question
Related Questions
Surface shader ParallaxOffset issue 1 Answer
Z value shader 1 Answer
How to Modify Standard Surface Shader's SpecCube? 1 Answer