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

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

曲面细分

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

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

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

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

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

            [maxvertexcount(3)]//几何着色器void geo (triangle v2g v[3],inout TriangleStream<g2f> tStream) {float4 barycenter = (v[0].vertex + v[1].vertex + v[2].vertex)/3;float3 normal = (v[0].normal + v[1].normal + v[2].normal)/3;v[0].normal = normal;v[1].normal = normal;v[2].normal = normal;g2f g0, g1, g2;g0.data = v[0];g1.data = v[1];g2.data = v[2];//g0.barycentricCoordinates = float3(0, 0, 1);g1.barycentricCoordinates = float3(0, 1, 0);g2.barycentricCoordinates = float3(1, 0, 0);tStream.Append(g0);tStream.Append(g1);tStream.Append(g2);tStream.RestartStrip();}fixed4 frag (g2f i) : SV_Target{fixed4 col = tex2D(_MainTex, i.data.uv);float3 barys = i.barycentricCoordinates;float3 deltas = fwidth(barys);float3 smoothing = deltas * _WireframeSmoothing;float3 thickness = deltas * _WireframeThickness;barys = smoothstep(thickness, thickness + smoothing, barys);float minBary = min(barys.x, min(barys.y, barys.z));return float4(lerp(_WireframeColor, col, minBary),1);//return col;}ENDCG} 

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

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

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

public RenderTexture Displace;
...void Start(){Displace = new RenderTexture(1024, 1024,0, RenderTextureFormat.ARGBFloat);Displace.enableRandomWrite = true;Displace.Create();...}

[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,接下来就是如何使用它。

public class createTexture : MonoBehaviour
{public ComputeShader cshader;private int kernelHandle;...void Start(){...kernelHandle = cshader.FindKernel("CSMain");}void Update(){cshader.SetTexture(kernelHandle, "Result", Displace);cshader.Dispatch(kernelHandle, 1024 / 8, 1024 / 8, 1);...}
}

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

将一个render texture传入作为RWTexture2D。

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

这部分参考了博文

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

#pragma kernel CSMain
#define PI 3.14159274fRWTexture2D<float4> Result;float time;
[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{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);
}

位移贴图

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

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

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

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

unity 位移贴图正弦波面相关推荐

  1. unity中凹凸贴图、法线贴图、视差贴图和位移贴图

    凹凸贴图 ((更多资源资料下载群:193521697邀请码:10026)) 对于凹凸贴图在计算机图形领域中的研究,最早开始于70年代末,至今已经有接近30年历史了.NormalMap只是一种目前很流行 ...

  2. 9、计算机图形学——纹理的应用(环境贴图、凸凹贴图、法线贴图以及位移贴图)

    一.环境贴图 比如下面的这张图,左边的是一张表示环境光的纹理,这个纹理表示屋子内部四面八方都是啥样,右边是一个茶壶, 将纹理映射到这个茶壶上,就能茶壶上渲染出周围的环境 还有这样的球形环境贴图(sph ...

  3. OpenGL 位移贴图实例

    OpenGL 位移贴图 先上图,再解答. 按下D键 完整主要的源代码 源代码剖析 先上图,再解答. 按下D键 完整主要的源代码 #include <vmath.h> #include &l ...

  4. GPU Gems2 - 8 使用距离函数的逐像素位移贴图

    [章节概览] 距离贴图(distance map)是一种在像素着色器中给对象添加小范围位移映射的技术.这章中详细介绍了使用距离函数的逐像素位移贴图(Per-Pixel Displacement Map ...

  5. 图形学基础 真假位移(法线贴图、凹凸贴图和位移贴图、向量位移贴图的对比与区别)

    写在前面: 本文是在阅读了知乎题目为<高度图,视差贴图(Bump-maps),置换贴图(displacement),法线贴图的本质>一文后,对翻译内容有些疑问,故而去查阅原文,做出的翻译. ...

  6. 解决Unity模型贴图反转的问题

    解决Unity模型贴图反转的问题 设置shader的贴图属性:tiling x为-1 参考链接:https://www.cnblogs.com/vivid-stanley/p/5494218.html

  7. DirectX11 With Windows SDK--34 位移贴图

    前言 在前面的章节中,我们学到了法线贴图和曲面细分.现在我们可以将这两者进行结合以改善效果,因为法线贴图仅仅只是改善了光照的细节,但它并没有从根本上改善几何体的细节.从某种意义上来说,法线贴图只是一个 ...

  8. Unity教程之-Unity光照贴图Lightmapping

    一直没有光照贴图的深入了解,只知道它与灯光比起可以降低drawCall,偶然看到一本外文书上有详细的资料,这里学习并分享一下,下面开始! 一:简要说明一下Unity光照贴图的概念 翻译会有各种偏差,这 ...

  9. Unity 法线贴图、高光贴图、Cube Map shader

    Unity 相关纹理贴图 写unity shader有些时候了,出于寂寞,拿出来晒晒吧!!!! 先看一下Unity 法线贴图.高光贴图.Cube Map shader最终效果: 说老实话,我不怎么喜欢 ...

最新文章

  1. 3寸以上java手机_7寸屏手机有哪些(堪称性价比之王的四款手机)
  2. R语言读写二进制文件:WriteBin()、readBin()
  3. 正则表达式 一切从简
  4. Eclipse引入spring约束详细教程
  5. 熟练掌握python是什么概念-想要熟练掌握Python元组?你需要了解这10件应知事项...
  6. vs2015上使用github进行版本控制
  7. 这样写交互说明,开发不会约你去爬山~
  8. deinstall 卸载grid_oracle 11g RAC手动卸载grid,no deinstall   .
  9. RabbitMQ 延迟队列,消息延迟推送
  10. java 虚类private 继承_Java经典面试36题和答案
  11. Java调用百度OCR文字识别的接口
  12. um是代表什么意思_女生约会心里都想什么?女生约会举动代表什么意思
  13. 音量已经调到100%,如何再调整
  14. 《『若水新闻』客户端开发教程》——16.添加广告
  15. swagger连接mysql数据库_一键生成数据库文档,堪称数据库界的Swagger,有点厉害!...
  16. 2020年计算机系毕业人数,2020年中国大学生各专业就业报告出炉:计算机类专业月薪最高!...
  17. SLAM notes
  18. 企业级需求管理工具选型报告(2020年3月20日)
  19. spring-cloud-starter-bus-kafka利用kafka消息总线实现动态刷新配置
  20. LaText Error:Environment aligned undefined.

热门文章

  1. WebGIS开发杂谈(一)——基于ArcGIS构建WebGIS概述
  2. 台式计算机摄像头怎么打开,台式机摄像头的打开方法
  3. 手机app定制开发流程图
  4. 线性代数 --- 投影Projection 六(向量在子空间上的投影)
  5. 判断磁盘是机械硬盘还是固态硬盘
  6. layui子页面创建一个新的页面
  7. 华云数据:做数字经济时代的信创云计算专家
  8. linux 文件夹tar.bz2压缩命令,使用tar命令提取(或解压缩)tar.bz2和tbz2文件的方法...
  9. 运维常见命令(三)-不解压查看bz2日志文件
  10. 对其他组的作品的评审意见