文章著作权归作者所有。转载请联系作者,并在文中注明出处,给出原文链接。
本系列原更新于作者的github博客,这里给出链接。

前言

经过前面两个章节的铺垫,我们对渲染以及Unity Shaderlab相关的知识已经有了大概的认识,接下来将要学习的就是Shader最重要的部分,SL(Shader Language),着色器语言。目前主流的着色器语言有HLSL,GLSL,Cg。三者在语法上也有诸多共通之处,选择一种学习即可。而在Unity中,主流是选择Cg作为着色器语言。在Shader编写的过程中,我们会经常穿梭在各个空间中,这里不对3D数学部分的前置知识作介绍,相关知识可从前面章节推荐的书籍学习。

在Shaderlab中,有三种着色器的书写方式。一种是Fixed-Function Shader,固定管线着色器。在这个着色器中,我们只能对渲染进行少量的配置,效果也很有限,在Unity 5.x之后的版本,Unity弃用了这种着色器。第二种是Surface Shader,表面着色器,这是Unity为我们提供的一种便于书写的方式,我们可以通过少量的代码,控制光照阴影等繁复的细节由Unity帮我们处理。新建一个Standard Surface Shader,可以看到里面只有50余行代码,但它包含了所有基础实现。最后一种,是Vertex/Fragment Shader,顶点/片元着色器,这是实现各种天马行空想象的最佳场所,当然,它的代码量以及复杂度也是最高的。而前两种shader也会被编译成对应的Vertex/Fragment Shader。这三种书写方式,都是在.shader文件中进行,组织方式上也是极为相似的。

这个系列的重点是Vertex/Fragment Shader。

CGPROGRAM

以之前模板的代码作为例子:

Shader "Blog/Start" {Properties {_MainTex ("Base (RGB)", 2D) = "white" {}_Color ("Color", Color) = (1, 1, 1, 1)}SubShader {CGINCLUDE#include "UnityCG.cginc"sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;struct a2v {float4 vertex : POSITION;float2 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float2 uv : TEXCOORD0;};v2f vert(a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target {fixed4 color;color = tex2D(_MainTex, i.uv);color *= _Color;return color;}ENDCGPass {CGPROGRAM#pragma vertex vert#pragma fragment fragENDCG}}FallBack "Diffuse"
}

在这个Shader中,出现了两个不同的代码块。首先第一个是CGINCLUDE代码块,它可以被放置在任何位置,甚至是整个Shader代码块的外部。在这个代码块中,我们可以编写那些需要重用的代码(如顶点着色器或片元着色器)。然后是CGPROGRAM代码块。这个代码块需要放在Pass块内,否则编译器会把这个Shader当成Surface Shader转而去检索surf()函数进而引起报错。这个代码块也是定义Vertex/Fragment Shader的地方。要保证,每个Pass都有且只有一个Vertex Shader和Fragment Shader。这两个Shader通过#pragma编译命令指定。接着是两个结构体:

struct a2v {float4 vertex : POSITION;float2 texcoord : TEXCOORD0;
};struct v2f {float4 pos : SV_POSITION;float2 uv : TEXCOORD0;
};

第一个是顶点着色器的输入结构体,a2v即Application To Vertex(应用阶段到顶点着色),在每一个变量的后面都跟了一个冒号说明,冒号后的是这个变量的Semantic(语义),语义是和GPU通信的桥梁,告诉GPU在这个变量中填充什么数据。float4 vertex : POSITION;告诉GPU,把顶点数据的POSITION(模型空间下的顶点坐标)输入到vertex变量中,float2 texcoord : TEXCOORD0;的意思是,把纹理坐标集0给texcoord变量使用。在着色器之间的数据传递都是艺考语义实现的,使用结构体只是为了代码组织更有条理。

v2f即Vertex To Fragment,这是顶点着色器的输出结构体,也是片元着色器的输入结构体。float4 pos : SV_POSITION;:SV指System Value,带有SV前缀的语义在管线中都有特殊的含义,SV_POSITION的含义是裁剪空间下的坐标。

为什么输出裁剪空间下的顶点坐标?

因为这个坐标接下来用于片元着色器,片元着色器需要的是光栅化后的坐标,也就是裁剪空间的坐标。

然后是顶点着色器部分:

v2f vert(a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;
}

这里只是做里简单的空间变换以及纹理映射。既然需要的是裁剪空间的坐标,那直接把输入的顶点坐标变换到裁剪空间即可。UnityObjectToClipPos()是Unity为我们提供的坐标空间转换函数。

// Tranforms position from object to homogenous space
inline float4 UnityObjectToClipPos(in float3 pos)
{
#if defined(STEREO_CUBEMAP_RENDER_ON)return UnityObjectToClipPosODS(pos);
#else// More efficient than computing M*VP matrix productreturn mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(pos, 1.0)));
#endif
}
inline float4 UnityObjectToClipPos(float4 pos) // overload for float4; avoids "implicit truncation" warning for existing shaders
{return UnityObjectToClipPos(pos.xyz);
}

可以看到,这个函数处理了一些差别并重载了两个版本,但本质上,都是MVP矩阵右乘顶点坐标的列向量形式。然后是TRANSFORM_TEX宏。

// Transforms 2D UV by scale/bias property
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)

可以看到这个宏计算了顶点对应的纹理采样位置,计算方式也对应了我们之前说到的_ST(Scale & Tiling,纹理缩放和偏移)相关知识点。然后是片元着色器部分:

fixed4 frag(v2f i) : SV_Target {fixed4 color;color = tex2D(_MainTex, i.uv);color *= _Color;return color;
}

片元着色器的最终目的是确定片元的像素颜色,即一个RGBA值。首先注意到的是SV_Target,它的语义是:这个着色器只返回一个值,这个值也就是片元的像素颜色值。此外,片元着色器还可以返回多个颜色,这时我们需要用到SV_TargetN语义,在这个情境下,SV_Target0是对应片元的像素颜色。例:

struct frag_output {fixed4 color0 : SV_Target0;fixed4 color1 : SV_Target1;fixed4 color2 : SV_Target2;
}frag_output frag(v2f i) {frag_output output;// ...return output;
}

回到模板的片元着色器内部,首先是color = tex2D(_MainTex, i.uv);tex2D(sampler2D texture, float2 uv);是Cg为我们提供的一个纹理采样函数,它将按照输入的uv采样输入的纹理texture,最后返回采样颜色。然后是color *= _Color;这里只是简单的把采样颜色和shader外部给定的颜色做乘法处理(叠加)。在场景中新建一个sphere,把这个shader添加到一个新的material,再把这个material挂到sphere上,不出意外没有报错的话,即可得到一个纯白色的球(由于没有任何光照阴影计算,也没有给纹理赋值)。这是我们第一个生效的shader。

转载于:https://www.cnblogs.com/Li-F/p/10806798.html

2.1:CGPROGRAM相关推荐

  1. U3D_Shader编程(第二篇:基础夯实篇)

    <U3D_Shader编程> ##<U3D_Shader编程>发布说明: ++++Shader一个高大上的领域,不管怎么样,我来了. ++++立钻哥哥从2018年开始正式对Sh ...

  2. 【GPU编程】The Cg Runtime:OpenGL中调用Cg程序

    原文:<The Cg Tutorial>(Cg手册):Appendix B:The Cg Runtime,Page195 很多地方可能翻译得不好,还请大家见谅.指教! 注意:我省略了其中的 ...

  3. 【GPU编程】开始Cg之旅,编译自己的第一个Cg程序

    Cg(C for Graphcis)语言,是NVIDIA 与Microsoft 合作研发,旨在为开发人员提供一套方便.跨平台(良好的兼容性),控制可编程图形硬件的高级语言.Cg 语言的语法结构与C 语 ...

  4. 【Unity Shaders】Lighting Models —— 灯型号Lit Sphere

    考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同一时候会加上一点个人理解或拓展. 这里是本书全部的插图.这里是本书所需的代码和资源(当然你 ...

  5. 【Unity Shaders】Mobile Shader Adjustment —— 为手机定制Shader

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  6. Unity Shader入门精要第七章 基础纹理之遮罩纹理

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 一.实践 参考 前言 遮罩纹理(mask texture)是本章要介绍的最后一种纹理,它非常有用,在很多商业游戏中 都可以见到它的身影. ...

  7. Unity3D Shader基础教程

    此教程将指引你如何建立自己的Shaders,让你的游戏场景看起来更好.Unity配备了强大的阴影和材料的语言工具称为ShaderLab,以程式语言来看,它类似于CgFX和Direct3D的语法,它不只 ...

  8. TA 认识 unity shader最基本的代码结构与书写01

    01:认识最简单的shader代码 Shader "Unlit/01minishader" { Properties { _MainTex ("Texture" ...

  9. unity入门精要之第6 章 Unity 中的基础光照--环境光和自发光

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 一.Unity 中的环境光和自发光 二.在Unity Shader 中实现漫反射光照模型 参考 前言 但这种模型有很多局限性.首先,有很 ...

最新文章

  1. python抽奖游戏_python实现转盘效果 python实现轮盘抽奖游戏
  2. Android文本框EditText显示为多行
  3. JavaScript 公有 私有 静态属性和方法
  4. Django 的模板语法之过滤器
  5. optee的RPC设计(模型)详解
  6. Ant Design入门之介绍
  7. 文本挖掘(part1)--文本挖掘概述
  8. hosts文件不起作用
  9. 城乡规划转到计算机专业行吗,哪些大学城乡规划专业有博士点
  10. VxWorks 6.9 内核编程指导之读书笔记 -- VxWorks kernel application (一)
  11. cin.ignore()函数的用法
  12. POJ2117 Electricity
  13. BZOJ 1013: [JSOI2008]球形空间产生器sphere
  14. matlab小波具体频段,一种小波包分解节点与对应频段检索及编程方法
  15. Angular端口4200被占用后如何处理
  16. 亚马逊后台付款表(Custom Transaction)详解
  17. 超级计算机排名太湖之光,发布超级计算机排名,中国“神威太湖之光”荣登榜首(印刷版)...
  18. 取消管理员取得所有权_取消管理员取得所有权|右键管理员取得所有权|win10获取管理员权限...
  19. 原生Python实现KNN算法,并用鸢尾花(iris)数据集测试
  20. 平生事,此时凝睇,谁会凭栏意!(4)

热门文章

  1. Java中ArrayList源码分析
  2. c++各种数据类型表示范围
  3. (转)AppCan中调用系统浏览器打开网页
  4. Fatal Error: Out of memory php内存溢出处理三种方法
  5. Quick Cocos2dx 初步战斗
  6. 工作中InnoDB引擎数据库主从复制同步心得
  7. 超大磁盘分区工具parted使用介绍(一)
  8. Android之解析Android Map地图返回的Json数据
  9. 自定义View合辑(8)-跳跃的小球(贝塞尔曲线)
  10. [Big Data - Kafka] kafka学习笔记:知识点整理