• unity 位移贴图正弦波面


    本文的目标是制作一个虽时间波动的正弦波面,效果如下图

     首先在unity中创建一个平面,这没什么可说的。

    曲面细分

    unity中默认的plane面数是很少的,不足以形成一个光滑的波面,所以第一步是进行曲面细分。

     如图所示,我们需要先输入原始的顶点数据,unity中会将他写成vertex shader的形式,但是实际上的vertex shader在后面。

    在hull program和domain program中我们可以设定细分的规则。曲面细分的具体原理在此不赘述,会用即可。需要注意的是在hullfun中的参数决定了细分的程度,这里我们直接为它赋值,更科学的方式可能是根据镜头的距离设定它的值。

    1. TessellationFactors hullFun (InputPatch<tessVertexData,3> v) {
    2. TessellationFactors o;
    3. o.edge[0] = _TessellationUniform;//设定的参数
    4. o.edge[1] = _TessellationUniform;
    5. o.edge[2] = _TessellationUniform;
    6. o.inside = _TessellationUniform;
    7. return o;
    8. }
    9. [UNITY_domain("tri")] 表示适用于三角形,还有quad(四边形)
    10. [UNITY_outputcontrolpoints(3)]//输出的控制点数量
    11. [UNITY_outputtopology("triangle_cw")]//输出拓扑结构为顺时针三角形,还有triangle_ccw(逆时针三角形)、line(线段)
    12. [UNITY_partitioning("fractional_odd")]//分数分割模式,还有integer(整数模式)
    13. [UNITY_patchconstantfunc("hullFun")]//细分函数
    14. //hull着色器:定义细分规则
    15. tessVertexData hul (InputPatch<tessVertexData,3> v, uint id : SV_OutputControlPointID) {
    16. return v[id];
    17. }
    18. [UNITY_domain("tri")]
    19. //domain着色器:计算细分后的顶点位置和数据,同时执行顶点着色器
    20. v2g dom (TessellationFactors tessFactors, const OutputPatch<tessVertexData,3> vi, float3 bary : SV_DomainLocation) {
    21. vertexData v;
    22. v.vertex = vi[0].vertex*bary.x + vi[1].vertex*bary.y + vi[2].vertex*bary.z;
    23. v.tangent = vi[0].tangent*bary.x + vi[1].tangent*bary.y + vi[2].tangent*bary.z;
    24. v.normal = vi[0].normal*bary.x + vi[1].normal*bary.y + vi[2].normal*bary.z;
    25. v.uv = vi[0].uv*bary.x + vi[1].uv*bary.y + vi[2].uv*bary.z;
    26. return vert (v);
    27. }

    为了清晰地看到网格,我们将网格以线条的方式渲染出来,渲染的原理是用几何着色器计算出每个三角网格的重心,根据三角形的重心坐标到三顶点的最小距离插值颜色。

    1. [maxvertexcount(3)]
    2. //几何着色器
    3. void geo (
    4. triangle v2g v[3],
    5. inout TriangleStream<g2f> tStream
    6. ) {
    7. float4 barycenter = (v[0].vertex + v[1].vertex + v[2].vertex)/3;
    8. float3 normal = (v[0].normal + v[1].normal + v[2].normal)/3;
    9. v[0].normal = normal;
    10. v[1].normal = normal;
    11. v[2].normal = normal;
    12. g2f g0, g1, g2;
    13. g0.data = v[0];
    14. g1.data = v[1];
    15. g2.data = v[2];
    16. //
    17. g0.barycentricCoordinates = float3(0, 0, 1);
    18. g1.barycentricCoordinates = float3(0, 1, 0);
    19. g2.barycentricCoordinates = float3(1, 0, 0);
    20. tStream.Append(g0);
    21. tStream.Append(g1);
    22. tStream.Append(g2);
    23. tStream.RestartStrip();
    24. }
    25. fixed4 frag (g2f i) : SV_Target
    26. {
    27. fixed4 col = tex2D(_MainTex, i.data.uv);
    28. float3 barys = i.barycentricCoordinates;
    29. float3 deltas = fwidth(barys);
    30. float3 smoothing = deltas * _WireframeSmoothing;
    31. float3 thickness = deltas * _WireframeThickness;
    32. barys = smoothstep(thickness, thickness + smoothing, barys);
    33. float minBary = min(barys.x, min(barys.y, barys.z));
    34. return float4(lerp(_WireframeColor, col, minBary),1);//
    35. return col;
    36. }
    37. ENDCG
    38. }

    以上部分参考了b站lyh萌主的文章https://www.bilibili.com/read/cv16290237

    计算着色器

    如标题所说,我们要用到一个随时间变化的位移贴图,显然它需要实时生成。可以选择在脚本的update函数中每次创建新的纹理,然后一个一个像素填充它。如果你这样做会发现帧率非常低,尤其当CPU性能不强时,因为CPU不擅长做这种并行计算。所以我们把这个任务交给GPU,这就用到了compute shader。

    虽然都是shader,但是unity中compute shader是用以 DirectX 11 样式 HLSL 语言编写的,所以与其他shader写法有所不同,也不在常规渲染管线中。

    下图是默认的compute shader,它包含了最重要的几部分。在这里插入图片描述

    #pragma kernel CSMain这一句可以理解为声明了CS中的某个函数(函数名为CSMain)。

    RWTexture2D Result;RW其实是Read和Write的意思,Texture2D就是二维纹理,因此它的意思就是一个可以被Compute Shader读写的二维纹理。CS是运行在GPU上的程序,并且独立于渲染管线,所以需要一个载体承担输入或输出。做渲染用途时,我们一般就将一个纹理作为载体。

    一般我们shader通常是只读的,大多使用的是sampler2D,然后通过tex2D函数已经UV坐标访问,但RWTexture2D的访问是直接通过Result[uint2(0,0)]来访问,值为float4型。

    由于这个纹理是需要读和写的,所以需要使用render texture,而不能是Texture2D。它的创建方式如下,注意需要开启它的读写,并调用create方法。

    1. public RenderTexture Displace;
    2. ...
    3. void Start()
    4. {
    5. Displace = new RenderTexture(1024, 1024,0, RenderTextureFormat.ARGBFloat);
    6. Displace.enableRandomWrite = true;
    7. Displace.Create();
    8. ...
    9. }

     [numthreads(8,8,1)]表示一个线程组的线程数量,即8*8*1,线程组的设定会影响计算效率,不过具体我也不太懂,有大佬懂得希望不吝赐教。在这我们让它保持默认。

    最后是核函数,重要的是它可以有几个输入的参数

    SV_GroupID:线程组的id
    SV_GroupIndex:即在每一个线程组元素里,线程的索引,[numthreads(8,8,1)],则索引范围(0, 0, 0) - (8, 8, 0),
    SV_DispatchThreadID:这个就是全局唯一的id,可以理解为一张图片的每个像素坐标

    所以上图中id.x id.y分别代表纹理上某个像素的横纵坐标。

    写完了一个CS,接下来就是如何使用它。

    1. public class createTexture : MonoBehaviour
    2. {
    3. public ComputeShader cshader;
    4. private int kernelHandle;
    5. ...
    6. void Start()
    7. {
    8. ...
    9. kernelHandle = cshader.FindKernel("CSMain");
    10. }
    11. void Update()
    12. {
    13. cshader.SetTexture(kernelHandle, "Result", Displace);
    14. cshader.Dispatch(kernelHandle, 1024 / 8, 1024 / 8, 1);
    15. ...
    16. }
    17. }

    我们需要定义一个compute shader,并为它的核函数定义一个索引(int类型)

    将一个render texture传入作为RWTexture2D。

    最后根据我们传入的texture大小(1024*1024)调用dispatch方法启动运算

    这部分参考了博文

    下面是实际用到的CS,我将位移量保存在纹理的R通道中,注意其中的常量PI最好使用宏定义

    1. #pragma kernel CSMain
    2. #define PI 3.14159274f
    3. RWTexture2D Result;
    4. float time;
    5. [numthreads(8,8,1)]
    6. void CSMain (uint3 id : SV_DispatchThreadID)
    7. {
    8. Result[id.xy] = float4((cos(id.x/ 1024.0f * 2 * PI*5+time) + cos(id.y/1024.0f * 2 * PI*5+time)) * 0.25f + 0.5f, 0, 0, 1);
    9. }

    位移贴图

    最后就是把位移贴图应用到渲染中。

    将compute shader计算好的texture传入shader,命名为_DisplaceTex,读出它的R通道,计算位移并加在法线方向上。

    注意这里对纹理的采样必须用tex2Dlod(),因为tex2D不能用在顶点着色器里(不知原因)。

    1. v2g vert(vertexData v)
    2. {
    3. ...
    4. float d = pow( tex2Dlod(_DisplaceTex, float4(v.uv.xy, 0, 0)).r, _Power) * _Displacement;
    5. v.vertex.xyz += v.normal * d;
    6. ...
    7. }

  • 相关阅读:
    2024年科技前瞻:AI辅助研发引领未来创新浪潮
    一文读懂uniapp中的tabBar底部导航
    记录一个删库跑路的技巧(如何快速删除数据库下面的所有表)
    数据结构与算法之图的应用
    数据结构-哈希表-哈希函数-哈希冲突
    【uniapp】使用canvas组件编译到微信小程序兼容出错问题
    设置单击右键可以选择用VS Code打开文件
    基础入门 - Spring Boot HelloWorld 第二节
    LeetCode-1774. 最接近目标价格的甜点成本【数组,背包问题,优化暴力,回溯】
    【Linux】进程数据结构
  • 原文地址:https://blog.csdn.net/qq_43533956/article/details/127408471