《着色器和屏幕特效》读书笔记第五章-顶点函数
2017-10-05 14:09
232 查看
本书版本为“占红来 译”版,笔记会持续更新,有错误的地方欢迎指正,谢谢!
三维模型和一堆三角形的集合,三角形的每个顶点可以包含一些基础数据,通过这些数据就能渲染出该模型了。本文要讲述的是:如何获取这部分信息并使用之。
float4类型的颜色值;
float3类型的顶点位置;
float3类型的顶点法线方向。
实现访问顶点颜色的代码:
导入Unity的模型的默认材质是不会让顶点颜色显示出来的,我们必须自己编写这个着色器获取到顶点颜色,并将它们展示在模型表面。
使用默认材质时:
使用含有该着色器的自定义的材质:
实现访问顶点位置并修改的代码:
未使用该着色器:
使用了该着色器:
实现法线挤压的代码:(特别简单,就是加一句:沿着法线增或减~)
正常图:
瘦的:
胖的:
再放出下面这张图,大家应该能全部理解了:
代码:
积雪效果:
积雪效果的详细制作请见这篇文章
补充:顶点函数只能修改顶点,而其他非顶点部分的信息通过插值得到。
要实现爆炸效果,需要一张坡度纹理(包含爆炸所需的所有颜色);需要一张噪声纹理(在着色器中获取随机数的最简单的方法:对噪声纹理进行采样)。
步骤一:通过顶点函数(这里的顶点函数是法线挤压的一个变种)修改几何结构;
步骤二:利用表面函数进行着色。
为了让爆炸更炫酷,还可以加一些粒子喔~
引言
着色器不仅能决定物体的外观,还能重新定义物体的形状。三维模型和一堆三角形的集合,三角形的每个顶点可以包含一些基础数据,通过这些数据就能渲染出该模型了。本文要讲述的是:如何获取这部分信息并使用之。
在表面着色器中访问顶点的颜色
一个顶点包含的信息:float4类型的颜色值;
float3类型的顶点位置;
float3类型的顶点法线方向。
实现访问顶点颜色的代码:
Shader "BookShaders/5-2 VertColor" { Properties { _MainTint("Global Color Tint",Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM //告诉Unity我们将在着色器中添加一个顶点函数。 #pragma surface surf Lambert vertex:vert #pragma target 3.0 float4 _MainTint; //Input结构里的数据是其他地方输出到它的数据,比如vertColor是vert()中输出到它的, //这实现了提取顶点颜色的目的,还要使surf()能访问之。所以这需要加一个vertColor。 struct Input { float2 uv_MainTex; float4 vertColor; }; //appdata_full是内置结构体,用来存储顶点信息。 void vert(inout appdata_full v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input, o);//相当于把全部都参数初始化了。 //appdata_full内置结构体的一个参数color。 o.vertColor = v.color; } //SurfaceOutput也是内置的结构体,用于输出信息到光照模型。surf()获取数据的两种方式: //1.通过“IN.XXXX”来使用Input结构里的数据;2.直接调用Properties中的属性值。 void surf (Input IN, inout SurfaceOutput o) { //Albedo是SurfaceOutput内置结构体的一个参数。 o.Albedo = IN.vertColor.rgb*_MainTint.rgb; } ENDCG } FallBack "Diffuse" }
导入Unity的模型的默认材质是不会让顶点颜色显示出来的,我们必须自己编写这个着色器获取到顶点颜色,并将它们展示在模型表面。
使用默认材质时:
使用含有该着色器的自定义的材质:
表面着色器中的顶点动画
访问顶点的每个位置并使用正弦波来动态修改每个顶点的位置,可用于制作飘扬的旗帜和海浪等效果。实现访问顶点位置并修改的代码:
Shader "BookShaders/5-3 VertexAnimation" { Properties { _MainTex("Base (RGB)", 2D) = "white" {} _tintAmount("Tint Amount", Range(0,1)) = 0.5 _ColorA("Color A", Color) = (1,1,1,1) _ColorB("Color B", Color) = (1,1,1,1) _Speed("Wave Speed", Range(0.1, 80)) = 5 _Frequency("Wave Frequency", Range(0, 5)) = 2 //振幅 _Amplitude("Wave Amplitude", Range(-1, 1)) = 1 } SubShader { Tags{ "RenderType" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert vertex:vert sampler2D _MainTex; float4 _ColorA; float4 _ColorB; float _tintAmount; float _Speed; float _Frequency; float _Amplitude; float _OffsetVal; struct Input { float2 uv_MainTex; float3 vertColor; }; void vert(inout appdata_full v, out Input o) { //相当于把参数初始化了。 UNITY_INITIALIZE_OUTPUT(Input, o); float time = _Time * _Speed; //使用正弦波对顶点位置进行修改。 float waveValueA = sin(time + v.vertex.x * _Frequency)*_Amplitude; //在y方向起伏 v.vertex.xyz = float3(v.vertex.x, v.vertex.y + waveValueA, v.vertex.z); //对网格顶点的法线进行微调,为了更逼真。让每个顶点的法线更偏向X轴的走向。 v.normal = normalize(float3(v.normal.x + waveValueA, v.normal.y, v.normal.z)); //可将下面三个数修改为(1,1,1)或(0,0,0)或(-1,-1,-1),就知道其作用了。 o.vertColor = float3(waveValueA, waveValueA, waveValueA); } void surf(Input IN, inout SurfaceOutput o) { half4 c = tex2D(_MainTex, IN.uv_MainTex); //lerp()函数混合两种颜色。凸起的地方(值为0到1)是_ColorB,凹陷的地方(值为0到-1)是_ColorA。 //使用vertColor、vertColor.r、vertColor.g、vertColor.b都是同样的效果,因为值都为waveValueA。 float3 tintColor = lerp(_ColorA, _ColorB, IN.vertColor).rgb; o.Albedo = c.rgb * (tintColor * _tintAmount); o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
未使用该着色器:
使用了该着色器:
挤压模型
通过法线挤压技术来控制模型的胖瘦(即调整其几何结构),比如:同一个模型(模型必须得有法线)可做成一个瘦瘦的僵尸或一个胖子。实现法线挤压的代码:(特别简单,就是加一句:沿着法线增或减~)
Shader "BookShaders/5-4 Extrusion" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} //用于表示挤压程度,0是不变,0到-0.00015是瘦,0到0.00015是胖。 //0.00015已经算是这个模型的阈值了,不然这模型就不是人样了。。。 _Amount("Extrusion Amount",Range(-0.00015,0.00015)) = 0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert vertex:vert #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; float _Amount; fixed4 _Color; void vert(inout appdata_full v) { //沿着法线方向进行挤压 v.vertex.xyz += v.normal*_Amount; } void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
正常图:
瘦的:
胖的:
再放出下面这张图,大家应该能全部理解了:
实现雪花着色器
步骤:先调整几何结构,再给表面着白色的雪。(顶点控制函数vert()会在表面着色函数surf()之前被执行,vert()的位置并不影响它的执行顺序。)代码:
Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _Bump ("Bump", 2D) = "bump" {} _Snow ("Snow Level", Range(0,1) ) = 0//_Snow决定多大的角度才算是朝着下雪的方向,即覆雪的范围。 _SnowColor ("Snow Color", Color) = (1.0,1.0,1.0,1.0)//雪的颜色 _SnowDirection ("Snow Direction", Vector) = (0,1,0)//下雪的方向(正上方) _SnowDepth ("Snow Depth", Range(0,0.3)) = 0.1//雪的厚度 } sampler2D _MainTex; sampler2D _Bump; float _Snow; float4 _SnowColor; float4 _SnowDirection; float _SnowDepth; struct Input { float2 uv_MainTex; float2 uv_Bump; float3 worldNormal; INTERNAL_DATA }; void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); //UnpackNormal(x)接受一个fixed4的输入x,并将x转换为所对应的法线值(fixed3)。 o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump)); //将三角形的法线从相对坐标转换为世界坐标后,再与下雪方向点积。 if (dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) > lerp(1,-1,_Snow)) { o.Albedo = _SnowColor.rgb; } else { o.Albedo = c.rgb; } o.Alpha = c.a; } //和之前积雪的运算其实比较类似,判断点积大小来决定是否需要扩大模型以及确定模型扩大的方向。 //surf是依靠三角形的法线和颜色rgba,vert是依靠顶点的法线和方向xyz。 void vert (inout appdata_full v) { float4 sn = mul(transpose(_Object2World) , _SnowDirection); if(dot(v.normal, sn.xyz) >= lerp(1,-1, (_Snow * 2) / 3)) { v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth * _Snow; } }
积雪效果:
积雪效果的详细制作请见这篇文章
实现体积爆炸效果
爆炸无非就是一团很热的气体,也就是模拟流体呗~很多游戏通过粒子效果来模拟爆炸,用一些火焰、烟雾和碎片粒子来做,而这种实现方式并不真实,所以有了体积(是三维对象)爆炸这个概念。补充:顶点函数只能修改顶点,而其他非顶点部分的信息通过插值得到。
要实现爆炸效果,需要一张坡度纹理(包含爆炸所需的所有颜色);需要一张噪声纹理(在着色器中获取随机数的最简单的方法:对噪声纹理进行采样)。
步骤一:通过顶点函数(这里的顶点函数是法线挤压的一个变种)修改几何结构;
步骤二:利用表面函数进行着色。
为了让爆炸更炫酷,还可以加一些粒子喔~
相关文章推荐
- 《着色器和屏幕特效》读书笔记第六章-碎片着色器和抓取
- 《着色器和屏幕特效》读书笔记第十章-高级着色技术
- 《着色器和屏幕特效》读书笔记第三章-光照模型
- 《着色器和屏幕特效》读书笔记第七章-移动端着色器优化
- 《着色器和屏幕特效》读书笔记第四章-PBR
- 第五章 类和函数:实现(Effective C++ Second Edition 读书笔记)
- 《lua程序设计》读书笔记 第五章:函数
- 《着色器和屏幕特效》读书笔记第一章-创建一个着色器
- Shader山下(十)表面着色器的顶点函数
- 如何在表面着色器中使用顶点函数
- 《着色器和屏幕特效》读书笔记第二章-表面着色器和纹理映射
- 《着色器和屏幕特效》读书笔记第八章-用Unity的渲染纹理实现屏幕特效
- 《着色器和屏幕特效》读书笔记第九章-游戏可玩性和屏幕特效
- 【读书笔记】 函数柯里化
- C++ Primer 读书笔记 - 第五章
- 《C++Primer》读书笔记--函数模板
- 《JavaScript高级程序设计》读书笔记--3-语句和函数
- 《中国人史纲》读书笔记:第四章 半信史时代 第五章 信史时代
- Unity3D - Shader - 顶点片段着色器(Vertex and Fragment Shaders)
- 《Pro Ogre 3D Programming》 读书笔记 之 第五章 场景管理 第二部分 (转)