[UnityShader基础]简单Shader本体
来源:互联网 发布:abs c语言 编辑:程序博客网 时间:2024/05/18 07:49
本文片段摘自:猫都能学会的Unity3D Shader入门指南,请点击链接查看原文,尊重楼主版权。
Shader本体
前面杂项说完了,终于可以开始看看最主要的部分了,也就是将输入转变为输出的代码部分。为了方便看,请容许我把上面的SubShader的主题部分抄写一遍
CGPROGRAM #pragma surface surf Lambertsampler2D _MainTex;struct Input { float2 uv_MainTex;};void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a;}ENDCG
还是逐行来看,首先是CGPROGRAM。这是一个开始标记,表明从这里开始是一段CG程序(我们在写Unity的Shader时用的是Cg/HLSL语言)。最后一行的ENDCG与CGPROGRAM是对应的,表明CG程序到此结束。
接下来是是一个编译指令:#pragma surface surf Lambert
,它声明了我们要写一个表面Shader,并指定了光照模型。它的写法是这样的:
#pragma surface surfaceFunction lightModel [optionalparams]
- surface - 声明的是一个表面着色器
- surfaceFunction - 着色器代码的方法的名字
- lightModel - 使用的光照模型。
所以在我们的例子中,我们声明了一个表面着色器,实际的代码在surf函数中(在下面能找到该函数),使用Lambert(也就是普通的diffuse)作为光照模型。
接下来一句sampler2D _MainTex;
,sampler2D是个啥?其实在CG中,sampler2D就是和texture所绑定的一个数据容器接口。等等..这个说法还是太复杂了,简单理解的话,所谓加载以后的texture(贴图)说白了不过是一块内存存储的,使用了RGB(也许还有A)通道,且每个通道8bits的数据。而具体地想知道像素与坐标的对应关系,以及获取这些数据,我们总不能一次一次去自己计算内存地址或者偏移,因此可以通过sampler2D来对贴图进行操作。更简单地理解,sampler2D就是GLSL中的2D贴图的类型,相应的,还有sampler1D,sampler3D,samplerCube等等格式。
解释通了sampler2D是什么之后,还需要解释下为什么在这里需要一句对_MainTex
的声明,之前我们不是已经在Properties
里声明过它是贴图了么。答案是我们用来实例的这个shader其实是由两个相对独立的块组成的,外层的属性声明,回滚等等是Unity可以直接使用和编译的ShaderLab;而现在我们是在CGPROGRAM...ENDCG
这样一个代码块中,这是一段CG程序。对于这段CG程序,要想访问在Properties
中所定义的变量的话,必须使用和之前变量相同的名字进行声明。于是其实sampler2D _MainTex;
做的事情就是再次声明并链接了_MainTex,使得接下来的CG程序能够使用这个变量。
终于可以继续了。接下来是一个struct结构体。相信大家对于结构体已经很熟悉了,我们先跳过之,直接看下面的的surf函数。上面的#pragma段已经指出了我们的着色器代码的方法的名字叫做surf,那没跑儿了,就是这段代码是我们的着色器的工作核心。我们已经说过不止一次,着色器就是给定了输入,然后给出输出进行着色的代码。CG规定了声明为表面着色器的方法(就是我们这里的surf)的参数类型和名字,因此我们没有权利决定surf的输入输出参数的类型,只能按照规定写。这个规定就是第一个参数是一个Input结构,第二个参数是一个inout的SurfaceOutput结构。
它们分别是什么呢?Input其实是需要我们去定义的结构,这给我们提供了一个机会,可以把所需要参与计算的数据都放到这个Input结构中,传入surf函数使用;SurfaceOutput是已经定义好了里面类型输出结构,但是一开始的时候内容暂时是空白的,我们需要向里面填写输出,这样就可以完成着色了。先仔细看看INPUT吧,现在可以跳回来看上面定义的INPUT结构体了:
struct Input { float2 uv_MainTex;};
作为输入的结构体必须命名为Input,这个结构体中定义了一个float2的变量…你没看错我也没打错,就是float2,表示浮点数的float后面紧跟一个数字2,这又是什么意思呢?其实没什么魔法,float和vec都可以在之后加入一个2到4的数字,来表示被打包在一起的2到4个同类型数。比如下面的这些定义:
//Define a 2d vector variablevec2 coordinate; //Define a color variablefloat4 color; //Multiply out a colorfloat3 multipliedColor = color.rgb * coordinate.x;
在访问这些值时,我们即可以只使用名称来获得整组值,也可以使用下标的方式(比如.xyzw,.rgba或它们的部分比如.x等等)来获得某个值。在这个例子里,我们声明了一个叫做uv_MainTex
的包含两个浮点数的变量。
如果你对3D开发稍有耳闻的话,一定不会对uv这两个字母感到陌生。UV mapping的作用是将一个2D贴图上的点按照一定规则映射到3D模型上,是3D渲染中最常见的一种顶点处理手段。在CG程序中,我们有这样的约定,在一个贴图变量(在我们例子中是_MainTex
)之前加上uv两个字母,就代表提取它的uv值(其实就是两个代表贴图上点的二维坐标 )。我们之后就可以在surf程序中直接通过访问uv_MainTex来取得这张贴图当前需要计算的点的坐标值了。
如果你坚持看到这里了,那要恭喜你,因为离最后成功读完一个Shader只有一步之遥。我们回到surf函数,它的两有参数,第一个是Input,我们已经明白了:在计算输出时Shader会多次调用surf函数,每次给入一个贴图上的点坐标,来计算输出。第二个参数是一个可写的SurfaceOutput,SurfaceOutput是预定义的输出结构,我们的surf函数的目标就是根据输入把这个输出结构填上。SurfaceOutput结构体的定义如下
struct SurfaceOutput { half3 Albedo; //像素的颜色 half3 Normal; //像素的法向值 half3 Emission; //像素的发散颜色 half Specular; //像素的镜面高光 half Gloss; //像素的发光强度 half Alpha; //像素的透明度};
这里的half和我们常见float与double类似,都表示浮点数,只不过精度不一样。也许你很熟悉单精度浮点数(float或者single)和双精度浮点数(double),这里的half指的是半精度浮点数,精度最低,运算性能相对比高精度浮点数高一些,因此被大量使用。
在例子中,我们做的事情非常简单:
half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a;
这里用到了一个tex2d
函数,这是CG程序中用来在一张贴图中对一个点进行采样的方法,返回一个float4。这里对_MainTex在输入点上进行了采样,并将其颜色的rbg值赋予了输出的像素颜色,将a值赋予透明度。于是,着色器就明白了应当怎样工作:即找到贴图上对应的uv点,直接使用颜色信息来进行着色,over。
接下来…
我想现在你已经能读懂一些最简单的Shader了,接下来我推荐的是参考Unity的Surface Shader Examples多接触一些各种各样的基本Shader。在这篇教程的基础上,配合一些google的工作,完全看懂这个shader示例页面应该不成问题。如果能做到无压力看懂,那说明你已经有良好的基础可以前进到Shader的更深的层次了(也许等不到我的下一篇教程就可以自己开始动手写些效果了);如果暂时还是有困难,那也没有关系,Shader学习绝对是一个渐进的过程,因为有很多约定和常用技巧,多积累和实践自然会进步并掌握。
- [UnityShader基础]简单Shader本体
- 【Unity Shader】UnityShader基础
- [UnityShader基础]GPU 与 Shader Model
- [UnityShader基础]Shader土地上的语言们
- UnityShader入门 积雪Shader
- UnityShader学习-纹理shader
- UnityShader基础
- [UnityShader]Shader上的优化
- [Shader] UnityShader系列---灰白效果
- UnityShader基础整理
- [UnityShader基础]Properties属性
- [UnityShader基础]Tags标签
- [UnityShader基础]LOD
- [UnityShader基础]基本概念
- [UnityShader基础]坐标系
- UnityShader——初探Compute Shader
- protege 新手入门 建立简单本体
- 本体
- python
- Maven
- 千行代码带你入门Python
- 桥接模式
- Educational Codeforces Round 31
- [UnityShader基础]简单Shader本体
- “玲珑杯”ACM比赛 Round #22 E【贪心】
- Java之Lists.Partition项目中的使用
- 【数位dp】B-number
- SpringMVC_统一异常处理
- 图像识别与处理之Opencv——像素值的读写
- otto框架简单的使用(个人笔记)
- 百度云视频CDN面试总结
- vb记录