有偿,求Unity3d温场云图unity3d着色器教程

Unity官方文档之“图形性能优化-优化着色器加载时间”的翻译,
Optimizing Shader Load Time 优化着色器加载时间
Shaders are small programs that execute on the GPU, and loading them can take some time. Each individual GPU program typically does not take much time to load, but shaders often have a lot of “variants” internally.
着色器是在GPU上执行的小程序,加载它们需要一些时间。每个独立的GPU程序一般不需要多少时间加载,但是着色器常常有很多内在的“变体”。
For example, the&, if fully compiled, ends up being many thousands of slightly different GPU programs. This creates two potential problems:
比如,如果完全编译,会生成几千个不同的小GPU程序。这会造成两个潜在的问题:
Large numbers of these shader variants increase game build time, and game data size.
Loading large numbers of shader variants during game is slow and takes up memory.
这些着色器的大量变体会增加游戏打包时间,使游戏包变大。
在游戏中加载大量的着色器变体很慢,并且消耗内存。
Shader build time stripping 着色器生成时去除
While building the game, Unity can detect that some of the internal shader variants are not used by the game, and skip them from build data. Build-time stripping is done for:
生成游戏包时,Unity可以检测到某些内在着色器变体没有被使用,然后忽略它们。生成时去除可用于:
Individual shader features, for shaders that use&#pragma shader_feature. If none of the used materials use a particular variant, then it is not included into the build. See&documentation. Out of built-in shaders, the&&uses this.
Shader variants to handle Fog and Lightmapping modes not used by any of the scenes are not included into the game data. See&&if you want to override this behavior.
个别着色器特性,使用#pragma着色器特性的着色器。如果一个变体没有被任何用到的材质使用,那么生成时就不把它打进去。参考,内置着色器中的使用这种方式。
没被任何场景使用的处理雾和光照贴图模式的着色器变体,也不会打包到游戏数据中。如果你想改变这个行为,请参考文档。
Combination of the above often substantially cuts down on shader data size. For example, a fully compiled Standard shader would take several hundred megabytes, but in typical projects it often ends up taking just a couple megabytes (and is often compressed further by the application packaging process).
以上方式的结合可以大大减少着色器数据的大小。比如,完全编译的标准着色器有几百兆,但是在一般的工程中常常只有几兆(而且app打包时会被进一步压缩)。
Default Unity shader loading behavior 默认Unity着色器加载行为
Under all default settings, Unity loads the&&object into memory, but does not create the&&until they are actually needed.
默认设置下,Unity加载对象到内存中,但是内部着色器变体直到被真正用到的时候才创建。
This means that shader variants that are included into the game build can still potentially be used, but there’s no memory or load time cost paid until they are needed. For example, shaders always include a variant to handle point lights with shadows, but if you never end up using a point light with shadows in your game, then there’s no point in loading this particular variant.
这意味着打进游戏包中的着色器变体潜在的可能被用到,但是在用到之前没有占用内存或者消耗时间加载。比如,着色器常常包含一个处理点光源阴影的变体,但是如果你的游戏里从来没有用到点光源阴影,那么加载这个变体就没必要。
One downside of this default behavior, however, is a possible hiccup for when some shader variant is needed for the first time - since a new GPU program code has to be loaded into the graphics driver. This is often undesirable during gameplay, so Unity has&ShaderVariantCollection&assets to help solve that.
然而,这种默认行为的一个弊端是一些着色器变体第一次被用到的时候可能会出现卡顿(hiccup)??因为需要加载一个新的GPU程序代码到图形驱动。在游戏中这是不能接受的,所以Unity有一个叫ShaderVariantCollection的资源来帮助解决这个问题。
Shader Variant Collections 着色器变体群
ShaderVariantCollection is an asset that is basically a list of&, and for each of them, a list of Pass types and shader keyword combinations to load.
ShaderVariantCollection基本上是一个列表资源,每次加载一个由通道类型和着色器关键字组合的列表。
Shader variant collection inspector& 着色器变体群检视器
To help with creating these assets based on actually used shaders and their variants, the editor can track which shaders and their variants are actually used. In Graphics Settings, there is a button to create a new ShaderVariantCollection out of currently tracked shaders, or to clear the currently tracked shader list.
为了帮助创建这些基于被真正用到的着色器和它们变体的资源,编辑器可以追踪哪些着色器和它们的变体被真正用到了。当前追踪着色器的图形设置检视器上,有一个按钮可以创建一个新的ShaderVariantCollection,也可以清除当前追踪的着色器列表。
Creating ShaderVariantCollection from shaders used by editor 从编辑器使用的着色器创建ShaderVariantCollection
Once you have some ShaderVariantCollection assets, you can set for these variants to be automatically preloaded while loading the game (under Preloaded Shaders list in&), or you can preload an individual shader variant collection from a script. See&&scripting class.
一旦有了一些ShaderVariantCollection资源,你可以设置这些着色器变体在加载游戏时自动预加载(在的预加载着色器列表下),或者你也可以用脚本预加载一个独立的着色器变体。参考脚本类。
See Also 另外可参考
. 中文翻译:Unity3D - 图形性能优化。
. 图形设置。
. 着色器参考。给我们留言
地址:福建省晋江市青阳街道洪山路国际工业设计园纳金网
电话: 400-067-3919
(周一到周五, 周六周日休息)
设计师入口
查看: 1183|回复: 6
最后登录注册时间阅读权限90积分31799
资深设计师, 积分 31799, 距离下一级还需 18201 积分
纳金币32712 精华79
最近做一个项目,涉及到温度云图(温场)的问题,论坛里搜索到有一两个发帖问的,但没有具体实现。小弟接触时间也不长,作了一些学习研究,写了一个简单的shader来实现温度云图。基本原理是给定平面上的几个点的位置和该点的温度信息,然后通过距离比的关系来算出平面上任意点的温度数值,再通过颜色反映出来。(本来还向算法大牛讨教了什么线性差值法,后来决得太复杂,从大牛那里求来了这个距离比的简易方法)
具体看http://dong2008hong.
Shader &Custom/TemperatureField& {
Properties {
_MainTex (&Base (RGB)&, 2D) = &white& {}
_Point1(&Temperature1&,Range(0,100)) = 50
_Point2(&Temperature2&,Range(0,100)) = 50
_Point3(&Temperature3&,Range(0,100)) = 50
_Point4(&Temperature4&,Range(0,100)) = 50
SubShader {
AlphaTest Greater 0.1
// Upgrade NOTE: excluded shader from DX11, Xbox360, OpenGL ES 2.0 because it uses unsized arrays
#pragma exclude_renderers d3d11 xbox360 gles
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include &UnityCG.cginc&
sampler2D _MainT
float4 _MainTex_ST;
float _Point1;
float _Point2;
float _Point3;
float _Point4;
bool computer =
struct v2f {
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
v2f vert(appdata_base v)
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
float computerTemperature(float2 uv)
int plength = 3;
float _midPointX[3] = {0.2,0.8,0.5};
float _midPointY[3] = {0.7,0.9,0.4};
float _midPointT[3] = {10,20,90};
float d1 = sqrt(uv.x*uv.x+uv.y*uv.y);
float d2 = sqrt((1-uv.x)*(1-uv.x)+(1-uv.y)*(1-uv.y));
float d3 = sqrt(uv.x*uv.x+(1-uv.y)*(1-uv.y));
float d4 = sqrt((1-uv.x)*(1-uv.x)+uv.y*uv.y);
float m = 1/d1+1/d2+1/d3+1/d4;
float n = 1/d1*_Point1+1/d2*_Point4+1/d3*_Point3+1/d4*_Point2;
for (int i = 0 ; i & i++)
float dp = sqrt((uv.x-_midPointX)*(uv.x-_midPointX)+(uv.y-_midPointY)*(uv.y-_midPointY));
m = m + 1/
n = n + 1/dp*_midPointT;
return n/m;
float4 frag(v2f i):COLOR
float4 texCol = tex2D(_MainTex,i.uv);
float temp = computerTemperature(i.uv);
//float temp = computeArray(i.uv);
//图像区域,判定设置为颜色的A & 0.5,输出为材质颜色+光亮值
if(texCol.w&0.5)
if(temp &= 60)
outp = float4(1,0,0,1)*(temp-60)/40+float4(1,1,0,1)*(1-(temp-60)/40);
else if(temp &= 30)
outp = float4(1,1,0,1)*(temp-30)/30+float4(0,1,0,1)*(1-(temp-30)/30);
outp = float4(0,1,0,1)*(temp)/30+float4(0,0,1,1)*(1-(temp)/30);
outp = float4(0,0,0,0);
FallBack &Diffuse&
其中_Point1到4是平面4个顶点上的温度值
_midPointX,_midPointY,_midPointT给出了平面内三个点的位置和温度值,实际应用中可以相应修改和增删
具体的请看:http://dong2008hong. ... 919750/
最后登录注册时间阅读权限50积分1086
助理设计师, 积分 1086, 距离下一级还需 914 积分
纳金币86 精华0
你的链接给这样 别人怎么看?
最后登录注册时间阅读权限90积分6187
高级设计师, 积分 6187, 距离下一级还需 3813 积分
纳金币138 精华0
great stuff
最后登录注册时间阅读权限90积分5676
高级设计师, 积分 5676, 距离下一级还需 4324 积分
纳金币7264 精华0
Thanks for this!!!
最后登录注册时间阅读权限90积分5659
高级设计师, 积分 5659, 距离下一级还需 4341 积分
纳金币470 精华0
好歹给点图啊
最后登录注册时间阅读权限90积分6667
高级设计师, 积分 6667, 距离下一级还需 3333 积分
纳金币2715 精华0
Thanks for this!
最后登录注册时间阅读权限70积分3154
中级设计师, 积分 3154, 距离下一级还需 1846 积分
纳金币1065 精华0
感谢分享!!!
站长推荐 /1
纳金名模第133期隔断3d模型:数量100个,大小为174MB。完全免费下载哦!这样的福利我和我的小伙伴们都惊呆了!!下载地址:
400-067-3919
Powered by - X2.5
Narkii Inc.[原]【浅墨Unity3D Shader编程】之六 暗黑城堡篇: 表面着色器(Surface Shader)的写法(一) - 推酷
[原]【浅墨Unity3D Shader编程】之六 暗黑城堡篇: 表面着色器(Surface Shader)的写法(一)
本系列文章由
@浅墨_毛星云&
出品,转载请注明出处。&
文章链接:
作者:毛星云(浅墨)& & &
本文主要讲解了Unity中SurfaceShader的具体写法,以及几个常用的CG函数的用法。
在这里先说明一下,表面着色器将分为两次讲解,本文介绍表面着色器的基本概念和一些写法,用内置的兰伯特光照模式来进行Surface Shader的书写,而下次将介绍Surface Shader+自定义的光照模式的写法。
PS:最近几天,在完美世界、腾讯互娱工作多年的几个朋友们问了浅墨一些表面着色器相关的Shader写法,浅墨当时回答他们的时候自己也是似懂非懂。通过这篇文章的书写,现在算是对这方面知识有了进一步的理解。所以说嘛,写作是总结自己所学的一种很好的方式~
OK,言归正传,依然是先来看看本文配套的游戏场景截图。
运行游戏,音乐响起,首先是一个欧式风格的集市映入眼帘:
雨淅沥沥、天空、晚霞、海平面:
天空中,一群飞鸟飞过:
暗黑城堡:
在小集市中逛荡:
城堡概况图:
OK,图先就上这么多。文章末尾有更多的运行截图,并提供了源工程的下载。可运行的exe下载在这里:
好的,我们正式开始。
一、表面着色器的标准输出结构(Surface Output)
要书写Surface Shader,了解表面着色器的标准输出结构必不可少。此为 表面着色器书写的第一个要素。
而定义一个“表面函数(surface function)”,需要输入相关的UV或数据信息,并在输出结构中填充SurfaceOutput。SurfaceOutput基本上描述了表面的特性(光照的颜色反射率、法线、散射、镜面等)。其实还是需要用CG或者HLSL编写此部分的代码。
我们其实是通过表面着色器(Surface Shader)来编译这段CG或者HLSL代码的,然后计算出需要填充输入什么,输出什么等相关信息,并产生真实的顶点(vertex)&像素(pixel)着色器,以及把渲染路径传递到正向或延时渲染路径。
说白了,还是那句话,Surface Shader是Unity微创新自创的一套着色器标准,是Unity自己发扬光大的一项使Shader的书写门槛降低和更易用的技术。
我们之前的文章中已经稍微了解过,表面着色器(Surface Shader)的标准输出结构是这样的:
struct SurfaceOutput
//反射率,也就是纹理颜色值(r,g,b)
//法线,法向量(x, y, z)
//自发光颜色值(r, g,b)
//镜面反射度
而这个结构体的用法,其实就是对这些需要用到的成员变量在surf函数中赋一下值,比如说这样:
//表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
//反射率,也就是纹理颜色值赋为(0.6, 0.6, 0.6)
o.Albedo= 0.6;
注意到Albedo是half3类型的。那么o.Albedo = 0.6和o.Albedo = float3(0.6,0.6,0.6)是等价的。
二、表面着色器的编译指令
表面着色器的编译指令为编写表面着色器的第二个要素。
表面着色器放在CGPROGRAM .. ENDCG块里面,就像其他的着色器一样。区别是:
其必须嵌在子着色器(SubShader)块里面。而不是Pass块里面。因为表面着色器( Surface shader)将在多重通道(multiple passes)内编译自己,而不是放在某个Pass中。
我们甚至可以这样说,如果你写表面着色器,用不到Pass代码块,一般直接在SubShader块中完成就行了。
使用的 #pragma surface...指令,以声明这是一个表面着色器。指令的句法是:
#pragma surface surfaceFunction lightModel[optionalparams]
所需参数的讲解:
surfaceFunction - 表示指定名称的Cg函数中有表面着色器(surface shader)代码。这个函数的格式应该是这样:void surf (Input IN,inout SurfaceOutput o), 其中Input是我们自己定义的结构。Input结构中应该包含所需的纹理坐标(texture coordinates)和和表面函数(surfaceFunction)所需要的额外的必需变量。
lightModel -使用的光照模式。内置的是Lambert (diffuse)和 BlinnPhong (specular)两种,一般习惯用Lambert,也就是兰伯特光照模式。而编写自己的光照模式我们将在下次更新中讲解。
可以根据自己的需要,进阶选这样的一些可选参数:
alpha -透明( Alpha)混合模式。使用它可以写出半透明的着色器。
alphatest:VariableName -透明( Alpha)测试模式。使用它可以写出 镂空效果的着色器。镂空大小的变量(VariableName)是一个float型的变量。
vertex:VertexFunction - 自定义的顶点函数(vertex function)。相关写法可参考Unity内建的Shader:树皮着色器(Tree Bark shader),如Tree Creator Bark、Tree Soft Occlusion Bark这两个Shader。
finalcolor:ColorFunction - 自定义的最终颜色函数(final color function)。 比如说这样:
#pragma surfacesurf Lambert finalcolor:mycolor。
相关Shader示例可见下文Shader实战部分的第五个Shader(纹理载入+颜色可调)。
exclude_path:prepass 或者 exclude_path:forward - 使用指定的渲染路径,不需要生成通道。
addshadow - 添加阴影投射 & 收集通道(collector passes)。通常用自定义顶点修改,使阴影也能投射在任何程序的顶点动画上。
dualforward - 在
渲染路径中使用&
fullforwardshadows - 在
渲染路径中支持所有阴影类型。
decal:add - 添加贴图着色器(decal shader) (例如: terrain AddPass)。
decal:blend - 混合半透明的贴图着色器(Semitransparent decal shader)。
softvegetation - 使表面着色器(surface shader)仅能在Soft Vegetation打开时渲染。
noambient - 不适用于任何环境光照(ambient lighting)或者球面调和光照(spherical harmonics lights)。
novertexlights - 在正向渲染(Forward rendering)中不适用于球面调和光照(spherical harmonics lights)或者每个顶点光照(per-vertex lights)。
nolightmap - 在这个着色器上禁用光照贴图(lightmap)。(适合写一些小着色器)
nodirlightmap - 在这个着色器上禁用方向光照贴图(directional lightmaps)。 (适合写一些小着色器)。
noforwardadd - 禁用正向渲染添加通道(Forward&rendering additive pass)。 这会使这个着色器支持一个完整的方向光和所有光照的per-vertex/SH计算。(也是适合写一些小着色器).
approxview - 着色器需要计算标准视图的每个顶点(per-vertex)方向而不是每个像索(per-pixel)方向。 这样更快,但是视图方向不完全是当前摄像机(camera) 所接近的表面。
halfasview - 在光照函数(lighting function)中传递进来的是half-direction向量,而不是视图方向(view-direction)向量。 Half-direction会计算且会把每个顶点(per vertex)标准化。这样做会提高执行效率,但是准确率会打折扣。
此外,我们还可以在 CGPROGRA内编写 #pragma debug,然后表面编译器(surface compiler)会进行解释生成代码。
三、表面着色器输入结构(Input Structure)
表面着色器书写的第三个要素是指明表面输入结构(Input Structure)。
Input 这个输入结构通常拥有着色器需要的所有纹理坐标(texture coordinates)。纹理坐标(Texturecoordinates)必须被命名为“uv”后接纹理(texture)名字。(或者uv2开始,使用第二纹理坐标集)。
可以在输入结构中根据自己的需要,可选附加这样的一些候选值:
float3 viewDir - 视图方向( view direction)值。为了计算视差效果(Parallax effects),边缘光照(rim lighting)等,需要包含视图方向( view direction)值。
float4 with COLOR semantic -每个顶点(per-vertex)颜色的插值。
float4 screenPos - 屏幕空间中的位置。 为了反射效果,需要包含屏幕空间中的位置信息。比如在Dark Unity中所使用的 WetStreet着色器。
float3 worldPos - 世界空间中的位置。
float3 worldRefl - 世界空间中的反射向量。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。 请参考这个例子:Reflect-Diffuse 着色器。
float3 worldNormal - 世界空间中的法线向量(normal vector)。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。
float3 worldR INTERNAL_DATA - 世界空间中的反射向量。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。为了获得基于每个顶点法线贴图( per-pixel normal map)的反射向量(reflection vector)需要使用世界反射向量(WorldReflectionVector (IN, o.Normal))。请参考这个例子: Reflect-Bumped着色器。
float3 worldN INTERNAL_DATA -世界空间中的法线向量(normal vector)。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。为了获得基于每个顶点法线贴图( per-pixel normal map)的法线向量(normal vector)需要使用世界法线向量(WorldNormalVector (IN, o.Normal))。
四、一些本次写Shader用到的CG函数讲解
本次Shader书写用到了四个CG着色器编程语言中的函数——
UnpackNormal、
saturate、 dot、tex2D。
下面将分别对其进行讲解。
4.1 UnpackNormal( )函数
UnpackNormal接受一个fixed4的输入,并将其转换为所对应的法线值(fixed3),并将其赋给输出的Normal,就可以参与到光线运算中完成接下来的渲染工作了。
一个调用示例:
o.Normal = UnpackNormal (tex2D (_BumpMap,IN.uv_BumpMap));
4.2 saturate( )函数
saturate的字面解释是浸湿,浸透。其作用其实也就是将取值转化为[0,1]之内的一个值。
其可选的原型如下:
float saturate(float x);
float1 saturate(float1 x);
float2 saturate(float2 x);
float3 saturate(float3 x);
float4 saturate(float4 x);
half saturate(half x);
half1 saturate(half1 x);
half2 saturate(half2 x);
half3 saturate(half3 x);
half4 saturate(half4 x);
fixed saturate(fixed x);
fixed1 saturate(fixed1 x);
fixed2 saturate(fixed2 x);
fixed3 saturate(fixed3 x);
fixed4 saturate(fixed4 x);
其唯一的一个参数x表示矢量或者标量的饱和值(Vector or scalar to saturate.),也就是将这个x转化为[0,1]之内的值。
其返回值:
如果x取值小于0,则返回值为0.
如果x取值大于1,则返回值为1.
若x在0到1之间,则直接返回x的值。
其代码实现大致如下:
float saturate(float x)
return max(0,min(1, x));
一个调用示例:
half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
4.3 dot( )函数
dot函数顾名思义,是高等数学中的点积操作,用于返回两个向量的标量积。
可选原型如下:
float dot(float a, float b);
float dot(float1 a, float1 b);
float dot(float2 a, float2 b);
float dot(float3 a, float3 b);
float dot(float4 a, float4 b);
half dot(half a, half b);
half dot(half1 a, half1 b);
half dot(half2 a, half2 b);
half dot(half3 a, half3 b);
half dot(half4 a, half4 b);
fixed dot(fixed a, fixed b);
fixed dot(fixed1 a, fixed1 b);
fixed dot(fixed2 a, fixed2 b);
fixed dot(fixed3 a, fixed3 b);
fixed dot(fixed4 a, fixed4 b);
其代码实现大致是这样的:
float dot(float4 a, float4 b)
return a.x*b.x +a.y*b.y + a.z*b.z + a.w*b.w;
一个调用示例:
float answer= dot (normalize(IN.viewDir),o.Normal);
4.4 tex2D( )函数
让我们看一看CG中用得比较多的用于2D纹理采样的tex2D函数的用法。其备选的原型也是非常之多:
float4 tex2D(sampler2D samp, float2 s)
float4 tex2D(sampler2D samp, float2 s, inttexelOff)
float4 tex2D(sampler2D samp, float3 s)
float4 tex2D(sampler2D samp, float3 s, inttexelOff)
float4 tex2D(sampler2D samp, float2 s,float2 dx, float2 dy)
float4 tex2D(sampler2D samp, float2 s,float2 dx, float2 dy, int texelOff)
float4 tex2D(sampler2D samp, float3 s,float2 dx, float2 dy)
float4 tex2D(sampler2D samp, float3 s,float2 dx, float2 dy, int texelOff)
int4 tex2D(isampler2D samp, float2 s)
int4 tex2D(isampler2D samp, float2 s, inttexelOff)
int4 tex2D(isampler2D samp, float2 s,float2 dx, float2 dy)
int4 tex2D(isampler2D samp, float2 s,float2 dx, float2 dy, int texelOff)
unsigned int4 tex2D(usampler2D samp, float2s)
unsigned int4 tex2D(usampler2D samp, float2s, int texelOff)
unsigned int4 tex2D(usampler2D samp, float2s, float2 dx, float2 dy)
unsigned int4 tex2D(usampler2D samp, float2s, float2 dx, float2 dy,int texelOff)
参数简介:
samp-需要查找的采样对象,也就是填个纹理对象在这里。
s-需进行查找的纹理坐标。
dx-预计算的沿X轴方向的导数。
dy-预计算的沿Y轴方向的导数。
texelOff-添加给最终纹理的偏移量
而其返回值,自然是查找到的纹理。
最后,看一个综合了本次讲解的四个函数(UnpackNormal、saturate、tex2D、dot)的Surface Shader中surf函数的示例:
//【2】表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
//从主纹理获取rgb颜色值
o.Albedo= tex2D (_MainTex, IN.uv_MainTex).
//从凹凸纹理获取法线值
o.Normal= UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
//从_RimColor参数获取自发光颜色
halfrim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
o.Emission= _RimColor.rgb * pow (rim, _RimPower);
五、写Shdaer实战
上面都是些概念,下面我们将进行一些实战的Shader书写,将学到的这些概念用到实际当中去。
本次我们将讲解9个表面SurfaceShader的写法,从最基本的Surface Shader,循序渐进,一点一点加功能,到最后的稍微有点复杂的“凹凸纹理+颜色可调+边缘光照+细节纹理“表面Shader的写法。本期的全部Shader的合照如下:
在材质界面菜单中的显示:
OK,下面开始讲解,从最基本的开始。
1.最基本的Surface Shader
先看一个使用内建光照模式的最基本的Surface Shader应该怎么写:
Shader &浅墨Shader编程/Volume6/24.最基本的SurfaceShader&
//--------------------------------【子着色器】----------------------------------
//-----------子着色器标签----------
Tags { &RenderType& = &Opaque& }
//-------------------开始CG着色器编程语言段-----------------
//【1】光照模式声明:使用兰伯特光照模式
#pragma surface surf Lambert
//【2】输入结构
struct Input
//四元素的颜色值(RGBA)
float4 color : COLOR;
//【3】表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
o.Albedo = float3(0.5,0.8,0.3);//(0.5,0.8,0.3)分别对应于RGB分量
//而o.Albedo = 0.6;等效于写o.Albedo = float3(0.6,0.6,0.6);
//-------------------结束CG着色器编程语言段------------------
//“备胎”为普通漫反射
Fallback &Diffuse&
可以发现,一个最基本的Surface Shader,至少需要有光照模式的声明、输入结构和表面着色函数的编写这三部分。
另外,主要注意其中的surf函数的写法,就是把上文讲到的Surface Output结构体中需要用到的成员变量拿来赋值:
//【2】表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
o.Albedo= float3(0.5,0.8,0.3);//(0.5,0.8,0.3)分别对应于RGB分量
//而o.Albedo = 0.6;等效于写o.Albedo =float3(0.6,0.6,0.6);
注释中已经写得很明白,且之前也已经讲过,o.Albedo = 0.6;等效于写o.Albedo = float3(0.6,0.6,0.6);
来个举一反三,则o.Albedo =1;等效于写o.Albedo= float3(1,1,1);
我们将此Shader编译后赋给材质,得到如下效果:
而在场景中的实测效果为:
2.颜色可调
在最基本的Surface Shader的基础上,加上一点代码,就成了这里的可调颜色的Surface Shader:
Shader &浅墨Shader编程/Volume6/25.颜色可调的SurfaceShader&
//--------------------------------【属性】---------------------------------------
Properties
_Color (&【主颜色】Main Color&, Color) = (0.1,0.3,0.9,1)
//--------------------------------【子着色器】----------------------------------
//-----------子着色器标签----------
Tags { &RenderType&=&Opaque& }
//-------------------开始CG着色器编程语言段-----------------
//【1】光照模式声明:使用兰伯特光照模式
#pragma surface surf Lambert
//变量声明
//【2】输入结构
struct Input
//四元素的颜色值(RGBA)
float4 color : COLOR;
//【3】表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
o.Albedo = _Color.
o.Alpha = _Color.a;
//-------------------结束CG着色器编程语言段------------------
//“备胎”为普通漫反射
FallBack &Diffuse&
我们将此Shader编译后赋给材质,得到如下效果,和之前的固定功能Shader一样,可以自由调节颜色:
其在场景中的实测效果为:
3.基本纹理载入
再来看看如何实现一个基本的纹理载入Surface Shader:
Shader &浅墨Shader编程/Volume6/26.基本纹理载入&
//--------------------------------【属性】----------------------------------------
Properties
_MainTex (&【主纹理】Texture&, 2D) = &white& {}
//--------------------------------【子着色器】----------------------------------
//-----------子着色器标签----------
Tags { &RenderType& = &Opaque& }
//-------------------开始CG着色器编程语言段-----------------
//【1】光照模式声明:使用兰伯特光照模式
#pragma surface surf Lambert
//【2】输入结构
struct Input
//纹理的uv值
float2 uv_MainT
//变量声明
sampler2D _MainT
//【3】表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
//从纹理获取rgb颜色值
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).
//-------------------结束CG着色器编程语言段------------------
//“备胎”为普通漫反射
Fallback &Diffuse&
我们将此Shader编译后赋给材质,得到如下效果:
场景中的实测效果图为:
4.凹凸纹理载入
让我们慢慢添加特性,使得到的Surface Shader的效果与功能越来越强大。接着来看看Surface Shader的凹凸纹理如何实现:
Shader &浅墨Shader编程/Volume6/27.凹凸纹理载入&
//--------------------------------【属性】----------------------------------------
Properties
_MainTex (&【主纹理】Texture&, 2D) = &white& {}
_BumpMap (&【凹凸纹理】Bumpmap&, 2D) = &bump& {}
//--------------------------------【子着色器】----------------------------------
//-----------子着色器标签----------
Tags { &RenderType& = &Opaque& }
//-------------------开始CG着色器编程语言段-----------------
//【1】光照模式声明:使用兰伯特光照模式
#pragma surface surf Lambert
//【2】输入结构
struct Input
//主纹理的uv值
float2 uv_MainT
//凹凸纹理的uv值
float2 uv_BumpM
//变量声明
sampler2D _MainT//主纹理
sampler2D _BumpM//凹凸纹理
//【3】表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
//从主纹理获取rgb颜色值
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).
//从凹凸纹理获取法线值
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
//-------------------结束CG着色器编程语言段------------------
//“备胎”为普通漫反射
Fallback &Diffuse&
我们将此Shader编译后赋给材质,得到如下效果:
场景中的实测效果图为:
5.纹理载入+颜色可调
接着看一看纹理如何通过一个finalcolor关键字自定义函数,来达到调色的目的:
Shader &浅墨Shader编程/Volume6/28.纹理+颜色修改&
//--------------------------------【属性】----------------------------------------
Properties
_MainTex (&【主纹理】Texture&, 2D) = &white& {}
_ColorTint (&【色泽】Tint&, Color) = (0.6, 0.3, 0.6, 0.3)
//--------------------------------【子着色器】----------------------------------
//-----------子着色器标签----------
Tags { &RenderType& = &Opaque& }
//-------------------开始CG着色器编程语言段-----------------
//【1】光照模式声明:使用兰伯特光照模式+自定义颜色函数
#pragma surface surf Lambert finalcolor:setcolor
//【2】输入结构
struct Input
//纹理的uv值
float2 uv_MainT
//变量声明
fixed4 _ColorT
sampler2D _MainT
//【3】自定义颜色函数setcolor的编写
void setcolor (Input IN, SurfaceOutput o, inout fixed4 color)
//将自选的颜色值乘给color
color *= _ColorT
//【4】表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
//从主纹理获取rgb颜色值
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).
//-------------------结束CG着色器编程语言段------------------
//“备胎”为普通漫反射
Fallback &Diffuse&
我们将此Shader编译后赋给材质,得到如下效果:
调些颜色玩一玩:
场景中的实测效果图为:
6. 凹凸纹理+边缘光照
在之前凹凸纹理的基础上让我们加上喜闻乐见的边缘光照:
Shader &浅墨Shader编程/Volume6/29.凹凸纹理+边缘光照&
//--------------------------------【属性】----------------------------------------
Properties
_MainTex (&【主纹理】Texture&, 2D) = &white& {}
_BumpMap (&【凹凸纹理】Bumpmap&, 2D) = &bump& {}
_RimColor (&【边缘颜色】Rim Color&, Color) = (0.26,0.19,0.16,0.0)
_RimPower (&【边缘颜色强度】Rim Power&, Range(0.5,8.0)) = 3.0
//--------------------------------【子着色器】----------------------------------
//-----------子着色器标签----------
Tags { &RenderType& = &Opaque& }
//-------------------开始CG着色器编程语言段-----------------
//【1】光照模式声明:使用兰伯特光照模式+自定义颜色函数
#pragma surface surf Lambert
//【2】输入结构
struct Input
//主纹理的uv值
float2 uv_MainT
//凹凸纹理的uv值
float2 uv_BumpM
//当前坐标的视角方向
float3 viewD
//变量声明
sampler2D _MainT//主纹理
sampler2D _BumpM//凹凸纹理
float4 _RimC//边缘颜色
float _RimP//边缘颜色强度
//【3】表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
//从主纹理获取rgb颜色值
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).
//从凹凸纹理获取法线值
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
//从_RimColor参数获取自发光颜色
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor.rgb * pow (rim, _RimPower);
//-------------------结束CG着色器编程语言段------------------
//“备胎”为普通漫反射
Fallback &Diffuse&
其中的viewDir 意为WorldSpace View Direction,也就是当前坐标的视角方向:
关于surf中的两句新加的代码在这里也讲一下。
上面已经提到过,Normalize函数,用于获取到的viewDir坐标转成一个单位向量且方向不变,外面再与点的法线做点积。最外层再用 saturate算出一[0,1]之间的最靠近的值。这样算出一个rim边界。原理可以这样解释:
这里o.Normal就是单位向量。外加Normalize了viewDir。因此求得的点积就是夹角的cos值。 因为cos值越大,夹角越小,所以,这时取反来。这样,夹角越大,所反射上的颜色就越多。于是就得到的两边发光的效果。哈哈这样明了吧。
这里再介绍一下这个half。CG里还有类似的float和fixed。half是一种低精度的float,但有时也会被选择成与float一样的精度。先就说这么多吧,后面还会遇到的,到时候再讲。
我们将此Shader编译后赋给材质,得到如下效果,除了凹凸纹理的选择之外,还有边缘发光颜色和强度可供自由定制:
依然是调一些颜色玩一玩:
场景中的实测截图为:
7.凹凸纹理+颜色可调
接下来我们看看凹凸纹理+颜色可调的Shader怎么写:
Shader &浅墨Shader编程/Volume6/30.凹凸纹理+颜色可调+边缘光照&
//--------------------------------【属性】----------------------------------------
Properties
_MainTex (&【主纹理】Texture&, 2D) = &white& {}
_BumpMap (&【凹凸纹理】Bumpmap&, 2D) = &bump& {}
_ColorTint (&【色泽】Tint&, Color) = (0.6, 0.3, 0.6, 0.3)
_RimColor (&【边缘颜色】Rim Color&, Color) = (0.26,0.19,0.16,0.0)
_RimPower (&【边缘颜色强度】Rim Power&, Range(0.5,8.0)) = 3.0
//--------------------------------【子着色器】----------------------------------
//-----------子着色器标签----------
Tags { &RenderType& = &Opaque& }
//-------------------开始CG着色器编程语言段-----------------
//【1】光照模式声明:使用兰伯特光照模式+自定义颜色函数
#pragma surface surf Lambert finalcolor:setcolor
//【2】输入结构
struct Input
//主纹理的uv值
float2 uv_MainT
//凹凸纹理的uv值
float2 uv_BumpM
//当前坐标的视角方向
float3 viewD
//变量声明
sampler2D _MainT
sampler2D _BumpM
fixed4 _ColorT
float4 _RimC
float _RimP
//【3】自定义颜色函数setcolor的编写
void setcolor (Input IN, SurfaceOutput o, inout fixed4 color)
color *= _ColorT
//【4】表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
//从主纹理获取rgb颜色值
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).
//从凹凸纹理获取法线值
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
//从_RimColor参数获取自发光颜色
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor.rgb * pow (rim, _RimPower);
//-------------------结束CG着色器编程语言段------------------
//“备胎”为普通漫反射
Fallback &Diffuse&
我们将此Shader编译后赋给材质,得到如下非常赞的效果。除了载入纹理,还有色泽,边缘颜色和强度可供调节:
依然是调些效果玩一玩:
而在场景中的实测效果为:
8.细节纹理
接着我们来看一个在屏幕上显示纹理细节的Shader:
Shader &浅墨Shader编程/Volume6/31.细节纹理&
//--------------------------------【属性】----------------------------------------
Properties
_MainTex (&【主纹理】Texture&, 2D) = &white& {}
_Detail (&【细节纹理】Detail&, 2D) = &gray& {}
//--------------------------------【子着色器】----------------------------------
//-----------子着色器标签----------
Tags { &RenderType& = &Opaque& }
//-------------------开始CG着色器编程语言段-----------------
//【1】光照模式声明:使用兰伯特光照模式
#pragma surface surf Lambert
//【2】输入结构
struct Input
//主纹理的uv值
float2 uv_MainT
//细节纹理的uv值
float2 uv_D
//变量声明
sampler2D _MainT
sampler2D _D
//【3】表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
//先从主纹理获取rgb颜色值
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).
//设置细节纹理
o.Albedo *= tex2D (_Detail, IN.uv_Detail).rgb * 2;
//-------------------结束CG着色器编程语言段------------------
//“备胎”为普通漫反射
Fallback &Diffuse&
我们将此Shader编译后赋给材质, 不加细节纹理如下:
加纹理细节的效果图如下:
而在场景中的实测效果为:
9.凹凸纹理+颜色可调+边缘光照+细节纹理
结合上面的8个Shader,我们可以完成本期文章这个结合了凹凸纹理+颜色可调+边缘光照+细节纹理的稍微复杂一点的Surface Shader:
Shader &浅墨Shader编程/Volume6/32.凹凸纹理+颜色可调+边缘光照+细节纹理&
Properties
_MainTex (&【主纹理】Texture&, 2D) = &white& {}
_BumpMap (&【凹凸纹理】Bumpmap&, 2D) = &bump& {}
_Detail (&【细节纹理】Detail&, 2D) = &gray& {}
_ColorTint (&【色泽】Tint&, Color) = (0.6, 0.3, 0.6, 0.3)
_RimColor (&【边缘颜色】Rim Color&, Color) = (0.26,0.19,0.16,0.0)
_RimPower (&【边缘颜色强度】Rim Power&, Range(0.5,8.0)) = 3.0
//--------------------------------【子着色器】----------------------------------
//-----------子着色器标签----------
Tags { &RenderType& = &Opaque& }
//-------------------开始CG着色器编程语言段-----------------
//【1】光照模式声明:使用兰伯特光照模式+自定义颜色函数
#pragma surface surf Lambert finalcolor:setcolor
//【2】输入结构
struct Input
//主纹理的uv值
float2 uv_MainT
//凹凸纹理的uv值
float2 uv_BumpM
//细节纹理的uv值
float2 uv_D
//当前坐标的视角方向
float3 viewD
//变量声明
sampler2D _MainT
sampler2D _BumpM
sampler2D _D
fixed4 _ColorT
float4 _RimC
float _RimP
//【3】自定义颜色函数setcolor的编写
void setcolor (Input IN, SurfaceOutput o, inout fixed4 color)
color *= _ColorT
//【4】表面着色函数的编写
void surf (Input IN, inout SurfaceOutput o)
//先从主纹理获取rgb颜色值
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).
//设置细节纹理
o.Albedo *= tex2D (_Detail, IN.uv_Detail).rgb * 2;
//从凹凸纹理获取法线值
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
//从_RimColor参数获取自发光颜色
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor.rgb * pow (rim, _RimPower);
//-------------------结束CG着色器编程语言段------------------
//“备胎”为普通漫反射
Fallback &Diffuse&
我们将此Shader编译后赋给材质,得到如下效果:
依然是调一些效果玩一玩:
而在场景中的实测效果为:
六、场景搭建
以大师级美工鬼斧神工的场景作品为基础,浅墨调整了场景布局,加入了音乐,并加入了更多高级特效,于是便得到了如此这次非常炫酷的暗黑城堡场景。
运行游戏,史诗级音乐渐渐响起,雨淅沥沥地下,我们来到了神秘的暗黑城堡:
在集市中逛荡1:
在集市中逛荡2:
在集市中逛荡3:
通向远方的桥梁:
壮观的瀑布:
感受瀑布溅起的水花:
在瀑布旁看远方,雾气氤氲:
成群的飞鸟飞过:
一片荒凉:
透过栅栏远眺:
画面太美,简直乱真:
海面上倒映出晚霞:
城堡概况:
最后一张本次的Shader全家福:
OK,美图就放这么多。游戏场景可运行的exe可以在文章开头中提供的链接下载。而以下是源工程的下载链接。
本篇文章的示例程序源工程请点击此处下载:
好的,本篇文章到这里就全部结束了。
下周浅墨要出差,所以下周是否更新得看具体的行程安排。
OK,新的游戏编程之旅正在继续, 我们下次再会~
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致}

我要回帖

更多关于 unity3d着色器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信