I've written a simple ray marcher in ShaderLab to visualize some 3D textures. Right now I'm applying the material to a cube and everything looks fine when the cube is at euler angles (0,0,0) but the more the cube is rotated the less predictable changes resulting from small movements in camera perspective become and the more two-dimensional the shader appears. I'm wondering how I would I best go about rotating 3D textures used in a ray marching shader, optimally I could just rotate the mesh itself and the shader would rotate intuitively along with it. I've tried rotating the uvw's in the shader itself but nothing seems to be working so I assume the problem lies in how i'm setting up my uvw's but I don't really know. At the moment here is how i'm setting my UVW's: Code (CSharp): void Calculate3DTexUVW() { List<Vector3> UVWs = new List<Vector3>(); for (int i = 0; i < meshFilter.sharedMesh.vertices.Length; i++) { UVWs.Add(new Vector3(meshFilter.sharedMesh.vertices[i].x, meshFilter.mesh.vertices[i].y, meshFilter.mesh.vertices[i].z)); if (UVWInWorldCoordinates) //toggle to make uvw's independant of mesh vertices { UVWs[i] = transform.TransformPoint(UVWs[i]); } } meshFilter.sharedMesh.SetUVs(0, UVWs); } Here is a version of the ray marcher: Code (CSharp): Shader "Custom/SpecieShaderPlane" { Properties { _MainTex3D("MainTex 3D", 3D) = "red" {} _Color("Specie Color", Color) = (1,1,1,1) _Intensity("Intensity", Range(0,1)) = 0.1 _Threshold("Min Threshold", Range(0,1)) = 0.5 //cut off alpha _StepSize("Penetration Step Size", Range(0,0.1)) = 0.05 _StepNumber("Step Number (times to propogate ray)", Range(0,512)) = 64 } SubShader { Tags { "Queue" = "Transparent" "RenderType" = "Transparent" } Blend SrcAlpha OneMinusSrcAlpha ZWrite On Cull Back Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "UnityShaderVariables.cginc" struct appdata { float4 mVert : POSITION; //model vertex position float3 UVW : TEXCOORD0; }; struct v2f { float3 UVWTransformed : TEXCOORD0; float4 cVert : SV_POSITION; //clip vertex position float4 wVert : POSITION1; //world vertex position float3 camToFragDir : POSITION2; //unit vector representing direction }; sampler3D _MainTex3D; float4x4 _UVWTransform; //set in c# script v2f vert(appdata input) { v2f o; o.cVert = UnityObjectToClipPos(input.mVert); //model-clip transform o.wVert = mul(unity_ObjectToWorld, input.mVert); //model-world transform o.UVWTransformed = mul(_UVWTransform, float4(input.UVW, 1)).xyz; //transform uvw o.camToFragDir = normalize(o.wVert - _WorldSpaceCameraPos); return o; } fixed4 _Color; float _Intensity; float _Threshold; float _StepSize; float _StepNumber; fixed4 frag(v2f input) : SV_Target { float3 ray = { 0,0,0 }; //offset from surface to sample from fixed4 col = { 0,0,0,0 }; //ray marching loop where a ray penetrates into a 3D texture and samples as it propagates for (int i = 0; i < _StepNumber; i++) { fixed4 currentSample = tex3D(_MainTex3D, input.UVWTransformed + ray); if (currentSample.r < _Threshold) { currentSample.rgb = 0; } col.a += (1 - col.a) * currentSample * _Intensity; // asymptotic transfer function ray += input.camToFragDir * _StepSize; //propogate ray } col.rgb = _Color.rgb; return col; } ENDCG } } } And here is how I was setting my uvw transform matrix in c# Code (CSharp): private void Transform(Vector3 pos, Vector3 rot, float sclInverse) { float t = Time.time; float scl = 1 / sclInverse; Quaternion rotQ = Quaternion.Euler(t * rot.x, t * rot.y, t * rot.z); Vector3 sclV = new Vector3(scl, scl, scl); Matrix4x4 mat = Matrix4x4.TRS(pos, rotQ, sclV); GetComponent<Renderer>().sharedMaterial.SetMatrix("_UVWTransform", mat); } Thanks!