10.Volumetric Shaders

本文详细介绍了体积渲染技术,包括Volumetric Shaders和Volumetric Rendering,阐述了如何从摄像机出发创建射线并进行内部延伸以检测图形表面。通过Shader代码展示了如何实现体渲染,特别提到了STEPS和STEP_SIZE的计算,以及WorldSpaceViewDir和UnityWorldSpaceViewDir的区别。此外,还讨论了如何利用纹理在渲染中创建体积雾效果,给出了具体的Shader实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

10. Volumetric Shaders

50. Volumetric Rendering体渲

体积渲染技术,是可以应用在一个形状里再画一个shap。

从正方体网格表面发射射线,碰到球表面像素(假想)变色。只着色视角方向的像素。

  1. 创建一个射线:从摄像机出发到达片元的世界坐标.
    1. 为什么要从摄像机出发?因为屏幕就是从摄像机位置出发。

2.从表面向内部按照steps延长射线。直到碰到所画图形表面。

  1. 如果射线击中假想球表面,则该片元返回球的颜色,否则更改位置,进入下一步长。
Shader "NiksShaders/Shader67Unlit"
{
    Properties
    {
        _Radius("Radius", Float) = 0.4
        _Center("Center", Vector) = (0,0,0,0)
        _SphereColor("Color", Color) = (0,0,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"

            #define STEPS 100 
            #define STEP_SIZE 0.0175

            float _Radius;
            float3 _Center;
            fixed4 _SphereColor;

            struct v2f {
                float4 position : SV_POSITION; // Clip space
                float4 worldPos : TEXCOORD1; // World position
            };
 
            // Vertex function
            v2f vert (appdata_base v)
            {
                 v2f o;
                 o.position = UnityObjectToClipPos(v.vertex);
                 o.worldPos = mul(unity_ObjectToWorld, v.vertex); 
                 return o;
            }
            fixed4 raymarch(float3 posiiton,float3 direction)
            {
                //通过这个循环来找到要涂色的点
                for (int i = 0;i < STEPS;i++)
                {
                    if (length(posiiton - _Center) < _Radius)return _SphereColor;
                    posiiton += direction * STEP_SIZE;//没找到就加上步长继续找
                }
                return fixed4(1,1,1,-0.1);
            }
            // Fragment function
            fixed4 frag (v2f i) : SV_Target
            {
                float3 viewDir = normalize(i.worldPos.xyz - _WorldSpaceCameraPos);
               //viewDir = normalize(-UnityWorldSpaceViewDir(i.worldPos.xyz));
                 fixed4 col = raymarch(i.worldPos,viewDir);
                clip(col);
                 return col;
            }
            ENDCG
        }
    }
    FallBack off
}

50.1 STEPS 和STEP_SIZE

  • STEPS用来控制步数,也就是精细程度

  • STEP_SIZE是具体算出来刚好覆盖最长边

  • 怎么算出来的呢?

    1. 因为立方体的最长边 1 2 + 1 2 + 1 2 = 3 ≈ 1.73 \sqrt{1^2 + 1^2 + 1^2} = \sqrt{3}\approx1.73 12+12+12 =3 1.73
    2. 然后我们控制 精细程度 STEPS = 100
    3. 就能得出STEP_SIZE = 0.0173

50.2 关于WorldSpaceViewDir和UnityWorldSpaceViewDir和透视除法

这里我修改了一下,因为默认的帮助参数_WorldSpaceCameraPos返回的就是float3,所以用i.worldPos.xyz

  • WorldSpaceViewDir(float4 vertex)也就是原生顶点。所以这个函数多用于顶点着色器,如果片元着色器要用就麻烦了,因为我们只有在v2f中定义了v.vertex后才有这个。

  • UnityWorldSpaceViewDir(float3 worldPos)也就是经过变换后的世界空间的坐标,是Unity改进后的函数,方便用于顶点和片元着色器,因为在顶点着色器中,我们通常啥原始数据都有,啥经过坐标变换的数据也有,所以顶点着色器对帮助函数兼容性很高主要解决片元着色器的使用问题,一般在v2f中都会定义worldPos这就方便了,直接传入就能使用。

  • 两者均返回从物体顶点射向摄像机的向量。

  • 这里我们使用的是从摄像机射向物体的,所以要取负。记不记得在49.2中我们使用reflect函数也取了负,去看一看吧?

  • 这里还有一点,就是经过帮助函数调用之后,返回的射线,向量,位置,大部分都是Vector3,很少有4了(如果得出的是仍需要进行计算的量,很有可能就有4,比如ComputeScreenPos,ComputeGrabScreenPos,计算出来之后得到的是剪裁空间的纹理,需要用透视除法,才能转换到屏幕空间

51. Using textures in the render

从正方体网格表面发射射线,

奇妙的0.5又出现了,除了在3.2中,这里floor算index也有.这是对于这个物体而言,中心实在(0.5,0.5,0.5)的,我们要将pos范围变到(0,1)方便uv计算。

利用贴图完成体积雾。

具体shader如下:

Shader "NiksShaders/Shader68Unlit"
{
    Properties
    {
        _Radius("Radius", Float) = 0.4
        _Center("Center", Vector) = (0,0,0,0)
        _Tex("Tex", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" }
        LOD 100

        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            ZWrite off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"

            float _Radius;
            float3 _Center;
            sampler2D _Tex;

            struct v2f {
                float4 position : SV_POSITION; // Clip space
                float3 worldPos : TEXCOORD1; // World position
            };
 
            // Vertex function
            v2f vert (appdata_base v)
            {
                 v2f o;
                 o.position = UnityObjectToClipPos(v.vertex);
                 o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; 
                 return o;
            }
            float raymarch(float3 position,float3 direction)
            {
                float alpha = 0;
                int steps = 144;
                float step_size = 1.0 / steps;
                for (int i = 0; i < steps; i++)
                {
                    int index = floor((position.z - _Center.z + 0.5) * steps);//这里算出来的z是在(0,1)之间的,然后乘以steps,算的是按深度使用哪张贴图
                    float2 uv = saturate(position.xy - _Center.xy + 0.5);//按照xy来算出uv是多少在(0,1)范围内
                    float2 offset = float2(fmod(index,12),floor(float(index)/12.0));//余数:x上第几张,商:y上的第几张,再加上uv就得出是哪个offset上的uv
                    //所以offset用来定位具体的小图片,uv是该图片上的纹理定位,但是这时按照正常大小算的,我们现在的图片是缩小了的,所以每边都要除以12来缩小。
                    //为什么不是144呢?注意,这里是float2
                    float2 newuv = (uv + offset)/12;
                    float texel = tex2D(_Tex,newuv).r;//r通道是1,则不透明
                    alpha += texel * step_size;//累加从表面开始射入的射线到达每个深度的alpha,最后结果反应在表面
                    position += direction * step_size;
                }
                return alpha;
            }
            // Fragment function
            fixed4 frag (v2f i) : SV_Target
            {
                float3 viewDirection = normalize(i.worldPos - _WorldSpaceCameraPos);
                float alpha = raymarch(i.worldPos,viewDirection);
                return fixed4(1,1,1,alpha);
            }
            ENDCG
        }
    }
    FallBack off
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值