【Unity3D Shader编程】之六 暗黑城堡篇 表面着色器 Surface Shader 的写法 一

本文详细介绍了Unity3D中Surface Shader的编写,包括表面着色器的标准输出结构、编译指令、输入结构及常用CG函数的用法。通过多个实例,逐步展示了从基础到进阶的Surface Shader应用,涉及颜色可调、纹理载入、凹凸纹理、边缘光照和细节纹理等效果。文章适合Unity3D初学者及希望深入理解Surface Shader的开发者阅读。
摘要由CSDN通过智能技术生成

分享一下我老师大神的人工智能教程。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

               


 



 本系列文章由@浅墨_毛星云 出品,转载请注明出处。  

 文章链接: http://blog.csdn.net/poem_qianmo/article/details/42215079

 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442

 邮箱: happylifemxy@163.com

 

 

本文主要讲解了Unity中SurfaceShader的具体写法,以及几个常用的CG函数的用法。

在这里先说明一下,表面着色器将分为两次讲解,本文介绍表面着色器的基本概念和一些写法,用内置的兰伯特光照模式来进行Surface Shader的书写,而下次将介绍Surface Shader+自定义的光照模式的写法。

 

PS:最近几天,在完美世界、腾讯互娱工作多年的几个朋友们问了浅墨一些表面着色器相关的Shader写法,浅墨当时回答他们的时候自己也是似懂非懂。通过这篇文章的书写,现在算是对这方面知识有了进一步的理解。所以说嘛,写作是总结自己所学的一种很好的方式~

 

OK,言归正传,依然是先来看看本文配套的游戏场景截图。


运行游戏,音乐响起,首先是一个欧式风格的集市映入眼帘:

 

 

 

 

雨淅沥沥、天空、晚霞、海平面:

 

 

天空中,一群飞鸟飞过:

 

 

 

峡谷:


 

暗黑城堡:

 


 

在小集市中逛荡:

 

 

城堡概况图:

 

 

OK,图先就上这么多。文章末尾有更多的运行截图,并提供了源工程的下载。可运行的exe下载在这里:

 

【可运行的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 {
         half3 Albedo;            //反射率,也就是纹理颜色值(r,g,b)    half3 Normal;            //法线,法向量(x, y, z)  half3 Emission;          //自发光颜色值(r, g,b)  half Specular;           //镜面反射度     half Gloss;              //光泽度    half Alpha;              //透明度};




而这个结构体的用法,其实就是对这些需要用到的成员变量在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 - 在正向(forward)渲染路径中使用 双重光照贴图(dual lightmaps)
    • fullforwardshadows - 在正向(forward)渲染路径中支持所有阴影类型。
    • 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 worldRefl; INTERNAL_DATA - 世界空间中的反射向量。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。为了获得基于每个顶点法线贴图( per-pixel normal map)的反射向量(reflection     vector)需要使用世界反射向量(WorldReflectionVector (IN,     o.Normal))。请参考这个例子: Reflect-Bumped着色器。
    • float3 worldNormal; INTERNAL_DATA -世界空间中的法线向量(normal vector)。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。为了获得基于每个顶点法线贴图(     per-pixel normal map)的法线向量(normal vector)需要使用世界法线向量(WorldNormalVector (IN, o.Normal))。

 

 



 

 

四、一些本次写Shader用到的CG函数讲解

 


<
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值