Write A simple raycast shader

user2535400 picture user2535400 · Oct 18, 2013 · Viewed 9k times · Source

I want to do a simple single-pass raycast shader. That is, i pass the vertex's world position in vertexshader, and in fragment shader start ray marching to Z axis(i use ortho projection) to get sample position in world coordinate, then i need to calculate the world's coordinate to model's coordinate to get the sample( texture) position in a 3d texture, where i am stuck in now. I did a same thing in cpp with opengl, i want to try it in unity3d and use benefit of gui. Code is down here, but it didn't work right.thanks for anyone's help, and sry for my poor english.

Shader "Customer/RayCast"
{
    Properties
    {
        _Volume ("Volume (Scalar)", 3D) = "white"{}
        //_VolumeGradient ("VolumeGradient (Scalar)", 3D) = "white"{}
        //_TransferMap_Red ("TransferMap_Red", 2D) = "white"{}
        //_TransferMap_Green ("TransferMap_Green", 2D) = "white"{}
        //_TransferMap_Blue ("TransferMap_Blue", 2D) = "white"{}
        //_TransferMap_Alpha ("TransferMap_Alpha", 2D) = "white"{}
        _Slider("slider", Float) = 0.2
    }
    SubShader 
    {
        Tags { "Queue" = "Transparent" } 
        Pass 
        {
        Cull Back
        Blend SrcAlpha OneMinusSrcAlpha
        CGPROGRAM
        // Upgrade NOTE: excluded shader from DX11 and Xbox360; has structs without          semantics (struct v2f members eye_pos)
        #pragma exclude_renderers d3d11 xbox360
        #pragma target 3.0
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"

        struct v2f
        {
            float4 pos : POSITION;
            float3 world_pos;
        };

        v2f vert(appdata_base v)
        {
            v2f output;
            output.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            output.world_pos = mul(_Object2World, v.vertex);
            return output;
        }

        sampler3D _Volume;
        float _Slider;
        float4 frag( v2f input ) : COLOR 
        {
            float3 start = input.world_pos;
            float4 dst = float4(0.0, 0.0, 0.0, 0.0);//final color.
            float3 ray_step = 0.01 * float3(0.0f, 0.0f, 1.0f);//camera look at positive z-            axis with ortho projection.
            float4 value;
            float3 sample_pos = start;
           float3 offset = float3(0.5f, 0.5f, 0.5f);//model vertex is -0.5 to 0.5, make it 0 to 1.0.
            for(int i = 0; i < 100; ++i)
            {
                sample_pos += (i * ray_step);
                value = tex3D(_Volume, mul(_World2Object, sample_pos).xyz + offset);
                value.a = 0.1f;
                value.xyz *= value.a;
                dst += (1.0 - dst.a) * value;
            }
           dst += float4(0.5f, 0.0f, 0.0f, 0.0f);
           return dst;
        }

        ENDCG
        }
     }
}

p.s.one more question is #program target 3.0 seems do not allowed the for loop to have more than 300 times, and in opengl i use 500 without problem with the same video card. And #program target 4.0 is not supported, Is there any way to improve it in unity3d?

Answer

user2535400 picture user2535400 · Oct 20, 2013

Thanks for Jerdak's explaination. My video card support SM4.0, but I can't only type #pragma target 4.0 in shader script. from http://docs.unity3d.com/Documentation/Components/SL-ShaderPrograms.html , it says

pragma target 4.0 - compile to DX10 shader model 4.0. Currently, this target is only supported by DirectX 11 renderer.

So I need to change renderer to dx11 in unity playersetting. Code below is a simple single-pass raycast shader, with low efficient.

Shader "Customer/RayCast" 
    {
    Properties{
    _Volume            ("Volume (Scalar)", 3D) = "white"{}
    _TransferMap_Red   ("TransferMap_Red", 2D) = "white"{}
    _TransferMap_Green ("TransferMap_Green", 2D) = "white"{}
    _TransferMap_Blue  ("TransferMap_Blue", 2D) = "white"{}
    _TransferMap_Alpha ("TransferMap_Alpha", 2D) = "white"{}
    }
    SubShader {
    Tags { "Queue" = "Transparent" } 
        Pass {
            Cull Back
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM 
            #pragma target 4.0
            #pragma vertex vert
            #pragma fragment frag 
            #include "UnityCG.cginc"
            
            struct v2f
            {
                float4 pos : POSITION;
                float3 world_pos : TEXCOORD;
            };

            v2f vert(appdata_base v)
            {
                v2f output;
                output.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                output.world_pos = mul(_Object2World, v.vertex).xyz;
                return output;
            }
            
            sampler3D _Volume;
            sampler2D _TransferMap_Red;
            sampler2D _TransferMap_Green;
            sampler2D _TransferMap_Blue;
            sampler2D _TransferMap_Alpha;
            float4 frag( v2f input ) : COLOR 
            {  
                float3 offset = float3(0.5f, 0.5f, 0.5f);
                float3 ray_step = 0.004f * float3(0.0f, 0.0f, 1.0f);//ortho camera look at positive z-axis.
                float3 start = input.world_pos;
                float4 dst = float4(0.0, 0.0, 0.0, 0.0);
                float value;
                float3 sample_pos = mul(_World2Object, float4(start.xyz, 1.0f)); 
                float4 sampled_color = float4(0.0f, 0.0f, 0.0f, 0.0f);
                for(int i = 0; i < 400; ++i)
                {
                    float3 world_pos = start + (i * ray_step);
                    sample_pos = mul(_World2Object, float4(world_pos.xyz, 1.0f)).xyz + offset;//cube from unity is -0.5 to 0.5.
                    value = tex3D(_Volume, sample_pos).a;
                    if(0.0 < sample_pos.x && 1.0 > sample_pos.x && 0.0 < sample_pos.y && 1.0 > sample_pos.y && 0.0 < sample_pos.z && 1.0 > sample_pos.z)
                    {
                        if(value > 0.02 && dst.a < 0.98)
                        {
                            sampled_color.r = tex2D(_TransferMap_Red,   float2(value, 0.5f)).a; 
                            sampled_color.g = tex2D(_TransferMap_Green, float2(value, 0.5f)).a;
                            sampled_color.b = tex2D(_TransferMap_Blue,  float2(value, 0.5f)).a;
                            sampled_color.a = tex2D(_TransferMap_Alpha, float2(value, 0.5f)).a;
                            dst = (sampled_color.a) * sampled_color + (1 - sampled_color.a) * dst;
                        }
                    }
                }
                if(dst.a < 0.05f)
                    dst.a = 0.0f;
                return dst;
            }

            ENDCG
        }
    }
}