1 前言

​ 上文介绍了渲染管线、固定管线着色器和表面着色器,如下:

​ 固定管线着色器通过命令方式实现光照和贴图等效果,表面着色器通过给 SurfaceOutput 赋值实现光照、贴图和法线贴图等效果,它们都不用关注光照算法是如何实现的,只需要传值就行。

​ 顶点和片元着色器给用户提供了更灵活的用法,但使用也更困难。另外,顶点着色器可以通过控制 MVP 矩阵变换实现对模型位置和姿态的控制。

2 固定颜色

​ 在 Assets 窗口右键,依次选择【Create→Shader→Standard Surface Shader】创建 Shader 脚本,实现固定颜色 Shader 代码如下:

​ VFShader.shader

Shader "MyShader/VFShaderTest" {
Properties
{
// 属性名 ("面板显示名称", 类型) = 默认值
_MainColor ("显示颜色", Color) = (1, 0, 0, 1)
} SubShader
{
Pass
{
CGPROGRAM // CG语言的开始
// 编译指令 着色器名称 函数名称
#pragma vertex vert // 顶点着色器, 每个顶点执行一次
#pragma fragment frag // 片段着色器, 每个像素执行一次
// 导入头文件
#include "UnityCG.cginc" // 声明属性变量, 必须与外部属性变量名称一致
fixed4 _MainColor; // 顶点着色器
float4 vert(float4 vertex: POSITION) : SV_POSITION
{
// 将局部坐标系下坐标转换为裁剪坐标系下坐标
return UnityObjectToClipPos(vertex); // 等价于: mul(UNITY_MATRIX_MVP, vertex)
} // 片元着色器
fixed4 frag(): COLOR
{
return _MainColor;
} ENDCG // CG语言的结束
}
} FallBack "Diffuse"
}

​ 创建一个 Material,并将 ShaderTest 绑定到该 Material 上,如下:

​ 将该 Material 拖拽到一个 Cube 和 Sphere 游戏对象上。选中绑定的 Material,在 Inspector 窗口调整 Shader 中固定颜色,显示效果如下:

3 光照

1)光照原理

​ Phong 光照模型和 Blinn Phong 光照模型是应用比较广泛的光照模型,两者区别在与镜面反射光的计算,Phong 光照模型根据反向量和观察向量计算镜面反射光,Blinn Phong 光照模型根据半向量和法向量计算镜面反射光。

​ 光照计算如下:

// 模型自身颜色
fixed4 albedo = tex2D(_MainTex, i.uv) * _ModelColor;
// 环境光
fixed4 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo;
// 漫反射光
fixed4 diffuse = _LightColor0 * albedo * max(0, dot(normal, lightDir));
// 镜面反射光(Phong光照模型)
// fixed4 specular = _LightColor0 * _Specular * pow(max(0, dot(reflectDir, viewDir)), _Gloss);
// 镜面反射光(Blinn Phong光照模型)
fixed4 specular = _LightColor0 * _Specular * pow(max(0, dot(normal, halfDir)), _Gloss);
// 合成颜色
fixed4 finalColor = fixed4(ambient + diffuse + specular, 1.0);

2)代码实现

​ VFShader.shader

Shader "MyShader/VFShaderTest" {
Properties {
_ModelColor ("Model Color", Color) = (1, 1, 1, 1) // 模型颜色
_Specular ("Specular Color", Color) = (1, 1, 1, 1) // 镜面反射颜色
_Gloss ("Gloss", Range(8.0, 256)) = 20 // 镜面反射光泽度
} SubShader {
Tags { "RenderType"="Opaque" } Pass {
Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc"
#include "Lighting.cginc" fixed4 _ModelColor; // 模型颜色
fixed4 _Specular; // 镜面反射颜色
float _Gloss; // 镜面反射光泽度 struct a2v {
float4 vertex : POSITION; // 模型空间顶点坐标
float3 normal : NORMAL; // 模型空间顶点法线向量
}; struct v2f {
float4 pos : SV_POSITION; // 裁剪空间顶点坐标
float3 normal : Normal; // 世界空间顶点法线向量
float3 worldPos : TEXCOORD0; // 世界空间顶点坐标
}; v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
o.normal = UnityObjectToWorldNormal(v.normal); // 将模型空间法线向量变换到世界空间
o.worldPos = mul(unity_ObjectToWorld, v.vertex); // 将模型空间顶点坐标变换到世界空间
return o;
} fixed4 frag(v2f i) : SV_Target {
fixed3 normal = normalize(i.normal); // 世界空间法线向量
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 世界空间灯光向量
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); // 世界空间观察向量
fixed3 halfDir = normalize(lightDir + viewDir); // 半向量
fixed3 albedo = _ModelColor; // 模型自身颜色
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo; // 环境光
fixed3 diffuse = _LightColor0 * albedo * max(0, dot(normal, lightDir)); // 漫反射光
fixed3 specular = _LightColor0 * _Specular * pow(max(0, dot(normal, halfDir)), _Gloss); // 镜面反射光(Blinn Phong光照模型)
return fixed4(ambient + diffuse + specular, 1);
} ENDCG
}
} FallBack "Specular"
}

​ 选中绑定的 Material,在 Inspector 窗口调整 Shader 中光照颜色,显示效果如下:

4 贴图

​ VFShader.shader

Shader "MyShader/VFShaderTest" {
Properties
{
// 属性名 ("面板显示名称", 类型) = 默认值
_MainTex ("2阶贴图", 2D) = "white" {}
} SubShader
{
Pass
{
Tags {"LightMode"="ForwardBase"}
CGPROGRAM // CG语言的开始
// 编译指令 着色器名称 函数名称
#pragma vertex vert // 顶点着色器, 每个顶点执行一次
#pragma fragment frag // 片段着色器, 每个像素执行一次 // 导入头文件
#include "UnityCG.cginc" // 声明属性变量, 必须与外部属性变量名称一致
sampler2D _MainTex; struct a2v // 顶点着色器输入结构体
{
float4 vertex : POSITION; // 模型空间顶点坐标
half2 texcoord : TEXCOORD0; // 纹理uv坐标
}; struct v2f // 顶点着色器输出结构体
{
float4 pos : SV_POSITION; // 裁剪空间顶点坐标
half2 uv : TEXCOORD0; // 纹理uv坐标
}; // 顶点着色器
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
o.uv = v.texcoord;
return o;
} // 片元着色器
fixed4 frag(v2f i) : SV_Target
{
return tex2D(_MainTex, i.uv);
} ENDCG // CG语言的结束
}
} FallBack "Diffuse"
}

​ 选中绑定的 Material,在 Inspector 窗口选择贴图图片,显示效果如下:

​ 声明:本文转自【Unity3D】顶点和片元着色器

【Unity3D】顶点和片元着色器的更多相关文章

  1. 【Android 应用开发】OpenGL ES 2.0 -- 制作 3D 彩色旋转三角形 - 顶点着色器 片元着色器 使用详解

    最近开始关注OpenGL ES 2.0 这是真正意义上的理解的第一个3D程序 , 从零开始学习 . 案例下载地址 : http://download.csdn.net/detail/han120201 ...

  2. OpenGL ES 2.0 -- 制作 3D 彩色旋转三角形 - 顶点着色器 片元着色器 使用详解

    最近开始关注OpenGL ES 2.0 这是真正意义上的理解的第一个3D程序 , 从零开始学习 . 案例下载地址 : http://download.csdn.net/detail/han120201 ...

  3. Unity3D学习笔记(三十五):Shader着色器(2)- 顶点片元着色器

    Alpha测试 AlphaTest Great:大于 AlphaTest Less:小于 AlphaTest Equal:等于 AlphaTest GEqual:大于等于 AlphaTest LEqu ...

  4. 片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但

    片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但片元着色器是一个更合适的名字, 因为此时的片元并不是一个真正意义上的像素.

  5. 《shader入门精要》13.2再谈运动模糊中片元着色器的世界坐标的计算

    具体在书p275页 这里为啥需要除D.w呢. 首先我们得到的NDC的坐标是已经归一化的,但是CurrenViewProjectionMatrix的作用,是把世界空间转化为尚未归一化的裁剪空间. 这里看 ...

  6. OpenGL ES着色器语言之语句和结构体(官方文档第六章)内建变量(官方文档第七、八章)

    OpenGL ES着色器语言之语句和结构体(官方文档第六章) OpenGL ES着色器语言的程序块基本构成如下: 语句和声明 函数定义 选择(if-else) 迭代(for, while, do-wh ...

  7. OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章)

    OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章) 4.5精度和精度修饰符 4.5.1范围和精度 用于存储和展示浮点数.整数变量的范围和精度依赖于数值的源(varying,unifo ...

  8. OpenGL ES着色器语言之变量和数据类型(一)(官方文档第四章)和varying,uniform,attribute修饰范围

    OpenGL ES着色器语言之变量和数据类型(一)(官方文档第四章)   所有变量和函数在使用前必须声明.变量和函数名是标识符. 没有默认类型,所有变量和函数声明必须包含一个声明类型以及可选的修饰符. ...

  9. OpenGL ES着色器语言之静态使用(static use)和预处理

    OpenGL ES着色器语言之静态使用(static use) 在OpenGL ES中有一个术语叫静态使用(static use),什么叫静态使用呢? 在写代码中,对于一个变量可能具有以下三种情况: ...

  10. OpenGLES2.0着色器语言glsl

    OpenGLES2.0中是强制使用可编程的渲染管线的,使用的是glsl着色器语言,因为着色器语言是使用的GPU,即图形处理单元,而不是CPU,这样可以使CPU从繁重的几何计算和像素的处理中解脱出来了. ...

随机推荐

  1. 解决windows系统电脑内存占用过高,一开机就是60%70%80%90%?

    1.问题 windows系统电脑内存占用过高,一开机就是60%70%80%90%? 2.解决方式 主要是虚拟内存一直没有及时释放导致的 先贴上B站视频链接:解决windows系统电脑内存占用过高 这里 ...

  2. [转帖]Nginx access log 按日期保存记录

    https://cloud.tencent.com/developer/article/1958304 $time_iso8601    生成格式:2021-09-18T15:16:35+08:00 ...

  3. [转帖]为什么需要在脚本文件的开头加上#!/ bin / bash?

    本文翻译自:Why do you need to put #!/bin/bash at the beginning of a script file? I have made Bash scripts ...

  4. java -D的一些学习和使用

    背景 java开发的程序有很多进行配置的方式 可以通过 yaml文件或者是xml文件 也可以通过环境变量的方式. 1. 容器的话可以使用 -e 或者是env进行注入 2. K8S的话可以通过 conf ...

  5. 神通奥斯卡数据库是否兼容Oracle, 以及参数修改的办法

    1. 最近公司要适配神通数据库, 但是因为一些功能异常.参数可能存在风险. 为了减少问题, 想着简单描述一下这些的处理. 开发和客户给的默认参数建议 1. 不选择 兼容oracle模式 2. 字符集选 ...

  6. 在K8S中各个组件及其作用?

    Kubernetes(简称K8s)是一个强大的容器编排系统,其主要组件及其作用如下: kube-apiserver: 集群的主入口点,提供了RESTful API接口供用户或内部组件与集群进行交互.它 ...

  7. 开源IM项目OpenIM单聊及万人群压测报告

    单聊压测结论: 华为云主机s3一台:8核16G内存,网络带宽10Mb,普通磁盘(非SSD) 同时在线及压测客户端数量:1万 每秒钟发送消息量:2300条: 从发送到对方接收平均消息延时:5秒 群聊压测 ...

  8. WebAssembly入门笔记[3]:利用Table传递引用

    在<WebAssembly入门笔记[2]>中,我们介绍了如何利用Memory在作为宿主的JavaScript应用和wasm模块之间传递数据,但是Momory面向单纯二进制字节的读写在使用起 ...

  9. 【主流技术】实战之 Spring Boot 中集成微信支付(小程序)

    前言 微信支付是企业级项目中经常使用到的功能,作为后端开发人员,完整地掌握该技术是十分有必要的. 以下是经过真实商业项目实践的集成步骤,包括注册流程.调用过程.代码demo(经过脱敏)等,希望我的分享 ...

  10. JavaScript获取浏览器的显示区域大小测试

    JavaScript获取浏览器的显示区域大小测试 Now we get the screen size about this browser 网页可见区域宽 document.body.clientW ...