- Home /
Can a shader make a texture ignore rotation when the gameobject that uses it is rotated?
To make my self clearer, I trying to make a shader that fixes the texture coordinates to a world position and not the local position so that the texture is always rendered in the same way even if the gameobject that uses it is being rotated. I don't even know if this is possible so I here is what I'm trying to do. I'm building lava pits by combining different lava tiles, such tiles have different configurations like a diagonal edge or a corner. With this approach I can build numerous pits with very few lava tiles configurations for optimization. Also my lava texture is seamless but only on one side. So with all this being said, my solution was to have a shader render the texture while ignoring the models rotation on the scene. Also I'm open to other solutions. Thanks
Answer by josetgg · Nov 02, 2016 at 06:05 PM
So tanoshimi was really helpful and I was able to find the solution to my problem. This is how my shader turned out. Ignore the animation mask which I haven't implemented yet. Basically is a lava shader part of system used for constructing lava pits using limited variations for optimization. This is for a mobile game so that is why we have the restrictions.
The shader has three textures:
One is a one direction seamless texture of lava(if you can call it that way),
The second is a mask that defines the lava texture alpha,
The third is a animation mask that is not implemented yet but is going to be used to know which vertices need to be animated.
This shader is an unlit vertex/fragment shader.
Shader "Custom/LavaUnlit"
{
Properties
{
_MainTex ("Lava Texture", 2D) = "white" {}
_TranspMask ("Transparency Mask", 2D) = "white" {}
_AnimMask ("Animation Mask", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _TranspMask;
fixed4 _MainTex_ST;
fixed4 _TranspMask_ST;
struct vertexInput
{
half4 vertex : POSITION;
half2 texcoord : TEXCOORD0;
};
struct vertexOutput
{
half4 pos : SV_POSITION;
half2 tex : TEXCOORD0;
half2 tex_mask : TEXCOORD1;
};
vertexOutput vert (vertexInput v)
{
vertexOutput o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
//this are the uv coordinates for the lava texture,
//using world space coordinates of the vertex allows
//to have mapping that ignores the models rotation
o.tex = TRANSFORM_TEX(mul(unity_ObjectToWorld,
v.vertex).xz, _MainTex);
//The transparency mask uses the models uv
//coordinates so that it actually rotates with the model
o.tex_mask = TRANSFORM_TEX(v.texcoord,
_TranspMask);
return o;
}
fixed4 frag (vertexOutput i) : COLOR
{
fixed4 mainTex = tex2D( _MainTex, i.tex);
fixed4 maskTex = tex2D( _TranspMask, i.tex_mask);
return fixed4(mainTex.rgb, maskTex.a);
}
ENDCG
}
}
}
Here is an image of the system implemented, with just six different lava prefabs we can build different shaped lava pit in an optimized way.
Ignore the assets their just placeholder assets that is why it doesn't looks seamless yet. Also the lava texture needs the wrap mode to be repeat if it's set to clamp the effect is not achieved
Answer by Namey5 · Nov 02, 2016 at 04:51 AM
What you would be looking at is a triplanar shader/procedural shader. What it does is it projects the texture from three sides and blends between them. Ordinarily this would be done in object space to avoid this effect, however it's easy enough to do a world space one. For example;
Shader "Tri-Planar World" {
Properties {
_Side("Side", 2D) = "white" {}
_Top("Top", 2D) = "white" {}
_Bottom("Bottom", 2D) = "white" {}
_SideScale("Side Scale", Float) = 2
_TopScale("Top Scale", Float) = 2
_BottomScale ("Bottom Scale", Float) = 2
}
SubShader {
Tags {
"Queue"="Geometry"
"IgnoreProjector"="False"
"RenderType"="Opaque"
}
Cull Back
ZWrite On
CGPROGRAM
#pragma surface surf Lambert
#pragma exclude_renderers flash
sampler2D _Side, _Top, _Bottom;
float _SideScale, _TopScale, _BottomScale;
struct Input {
float3 worldPos;
float3 worldNormal;
};
void surf (Input IN, inout SurfaceOutput o) {
float3 projNormal = saturate(pow(IN.worldNormal * 1.4, 4));
// SIDE X
float3 x = tex2D(_Side, frac(IN.worldPos.zy * _SideScale)) * abs(IN.worldNormal.x);
// TOP / BOTTOM
float3 y = 0;
if (IN.worldNormal.y > 0) {
y = tex2D(_Top, frac(IN.worldPos.zx * _TopScale)) * abs(IN.worldNormal.y);
} else {
y = tex2D(_Bottom, frac(IN.worldPos.zx * _BottomScale)) * abs(IN.worldNormal.y);
}
// SIDE Z
float3 z = tex2D(_Side, frac(IN.worldPos.xy * _SideScale)) * abs(IN.worldNormal.z);
o.Albedo = z;
o.Albedo = lerp(o.Albedo, x, projNormal.x);
o.Albedo = lerp(o.Albedo, y, projNormal.y);
}
ENDCG
}
Fallback "Diffuse"
}
Obtained from https://gist.github.com/radiatoryang/4725041
I'm not sure there's any need for a triplanar shader - a simple shader using worldspace coords ins$$anonymous$$d of mesh uvs would be sufficient if the lava is a simple flat "floor" of the level.
Thanks for the help Namey5, I'll try triplanar shader approach. Also tanoshimi the lava indeed is a simple flat object., so what you mean is to transform the vertex to world space and use it as the coordinates?
Exactly. Just use IN.worldPos.xz where hou would have use IN.input.uv
the mapping was being done incorrectly because I was missing the .xz. Thank you for the help. I don't $$anonymous$$now how should I mark it. I didn't get to try the triplanar approach. I'm just going to add the answer with my shader code.