【问题标题】:Write A simple raycast shader编写一个简单的光线投射着色器
【发布时间】:2018-03-15 08:49:36
【问题描述】:

我想做一个简单的单通道光线投射着色器。也就是说,我在vertexshader中传递顶点的世界位置,并在片段着色器中开始光线行进到Z轴(我使用正交投影)以获得世界坐标中的样本位置,然后我需要计算世界坐标到模型坐标以获得样本(纹理)在 3d 纹理中的位置,我现在被困在那里。 我用opengl在cpp中做了同样的事情,我想在unity3d中尝试它并使用gui的好处。 代码在这里,但它不能正常工作。感谢任何人的帮助,并为我糟糕的英语感到抱歉。

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.还有一个问题是#program target 3.0 似乎不允许 for 循环超过 300 次,而在 opengl 中,我在同一张显卡上使用 500 次没有问题。并且不支持#program target 4.0,请问unity3d有什么办法改进吗?

【问题讨论】:

  • 我们说的是哪种着色器语言,GLSL、HLSL、Cg?
  • 参见#include "UnityCG.cginc" 这一行。这是一个主要基于 CG 语法的着色器脚本,但具有统一的扩展,如属性、预定义和内置变量,如 appdata_base(struct appdata_base: vertex shader input with position, normal, one texture coordinates.)
  • 我尝试将“TEXCOORD”语义添加到 world_pos,使 start_pos 正确。但行军程序不对。 Unity 的日志没有正确显示着色器的错误消息。forloop 计数和寄存器限制真的很困扰我。
  • 那么,究竟是什么“工作不正常”?屏幕截图可能会有所帮助。至于着色器目标,如果不缩短代码,就无法绕过限制。听起来您正在达到说明的上限。你可能在 opengl 中有一个更大的循环,因为它没有 Unity 的所有底层着色器代码。
  • 谢谢你的回复。我终于自己弄明白了。

标签: unity3d shader volume-rendering


【解决方案1】:

感谢 Jerdak 的解释。 我的显卡支持 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。 下面的代码是一个简单的单通道光线投射着色器,效率低。

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

【讨论】:

  • for 在片段着色器中复杂度为 400 的循环.....再见,高 FPS。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多