Напишите простой рейкаст-шейдер

Я хочу сделать простой однопроходный шейдер raycast. То есть я передаю мировую позицию вершины в вершинном шейдере, а в фрагментном шейдере начинаю луч, направляющийся к оси Z (я использую орто-проекцию), чтобы получить позицию выборки в мировых координатах, затем мне нужно вычислить мировую координату для координаты модели, чтобы получить положение образца (текстуры) в 3D-текстуре, где я сейчас застрял. Я сделал то же самое в cpp с opengl, я хочу попробовать это в unity3d и использовать преимущества графического интерфейса. Код находится здесь, но он работает неправильно. Спасибо за помощь и извините за мой плохой английский.

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
        }
     }
}

ps еще один вопрос: #program target 3.0, кажется, не позволяет циклу for иметь более 300 раз, а в opengl я без проблем использую 500 с той же видеокартой. И #program target 4.0 не поддерживается. Есть ли способ улучшить его в unity3d?


person user2535400    schedule 18.10.2013    source источник
comment
О каком языке шейдеров идет речь, GLSL, HLSL, Cg?   -  person vallentin    schedule 18.10.2013
comment
См. строку #include UnityCG.cginc. Это сценарий шейдера, в основном основанный на синтаксисе CG, но с расширениями Unity, такими как свойства, предварительные определения и встроенные переменные, такие как appdata_base (struct appdata_base: ввод вершинного шейдера с позицией, нормалью, одной координатой текстуры.)   -  person user2535400    schedule 18.10.2013
comment
Я попытался добавить семантику TEXCOORD в world_pos, сделать start_pos правильным. но походная процедура не правильная. Журнал Unity не показывает сообщения об ошибках шейдера должным образом. Меня очень беспокоит количество циклов for и ограничения регистров.   -  person user2535400    schedule 18.10.2013
comment
Так что именно не так получилось? Скриншот может помочь. Что касается цели шейдера, вы не можете обойти ограничения, не укоротив свой код. Похоже, вы достигли верхнего предела в инструкциях. Вы, вероятно, могли бы иметь больший цикл в opengl, потому что у него не было всего кода шейдера Unity под капотом.   -  person Jerdak    schedule 20.10.2013
comment
Спасибо за ваш ответ. Наконец-то я понял это сам.   -  person user2535400    schedule 20.10.2013
comment
Во-первых, мне нужно добавить семантику TEXCOORD в файл world_pos. Во-вторых, в единстве найдите playsetting и установите для использования рендерер Directx11, тогда я смогу использовать шейдермод 4.0 без ограничений, о которых я сказал. Осталась одна странность, я не могу использовать break в шейдере, иначе единство зависнет при компиляции шейдера.   -  person user2535400    schedule 20.10.2013


Ответы (1)


Спасибо за объяснение Джердака. Моя видеокарта поддерживает SM4.0, но я не могу набрать только #pragma target 4.0 в шейдерном скрипте. из http://docs.unity3d.com/Documentation/Components/SL-ShaderPrograms.html , там написано

pragma target 4.0 — компилировать в модель шейдера DX10 4.0. В настоящее время эта цель поддерживается только средством визуализации DirectX 11.

Поэтому мне нужно изменить рендерер на dx11 в настройках проигрывателя Unity. Код ниже представляет собой простой однопроходный шейдер raycast с низкой эффективностью.

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
        }
    }
}
person user2535400    schedule 20.10.2013
comment
for цикл со сложностью 400 во фрагментном шейдере..... до свидания, высокий FPS. - person AgentFire; 12.08.2015