当前位置: 首页 > 编程日记 > 正文

2.1:CGPROGRAM

文章著作权归作者所有。转载请联系作者,并在文中注明出处,给出原文链接。
本系列原更新于作者的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。

Sphere for First Shader

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

相关文章:

【OpenCV】OpenCV中积分图函数与应用

OpenCV中积分图函数与应用 参考资料 opencv 查找integral,目前网上大部分的资料来自于opencv https://docs.opencv.org/master/d7/d1b/group__imgproc__misc.html#gadeaf38d7701d7ad371278d663c50c77dhttps://blog.csdn.net/jia20003/article/details/52710751ht…

django学习笔记【003】创建第一个带有model的app

【1】python应用程序要连接mysql有多个驱动程序可供选择: 1、MySQLdb 这个只支持python2.x 所以在这里就不说了; 2、mysqlclient 下载地址   https://pypi.python.org/pypi/mysqlclient/1.3.9 3、MySQL Connector/python 这个是mysql官方主推的mysql驱…

图像非极大值抑制 Sobel 边缘实现

bool SobelVerEdge(cv::Mat srcImage, cv::Mat& resultImage) {CV_Assert(srcImage.channels() 1);srcImage.convertTo(srcImage, CV_32FC1);// 水平方向的 Sobel 算子cv::Mat sobelx (cv::Mat_<float>(3,3) << -0.125, 0, 0.125,-0.25, 0, 0.25,-0.125, 0, …

第四次作业 (日期和jieba库的运用)

设计题1&#xff1a; 设计一个本月份日历&#xff0c;输出格式如下&#xff1a; 要求&#xff1a; 1.初始化start_day&#xff0c;end_day两个日期 from datetime import datetime start_daydatetime(2019,4,1) end_daydatetime(2019,4,30) 其它时间数据生成要用datetime或date…

【C++】LINK类型错误分析记录

LINK类型错误 情况1&#xff1a; 根据生成路径&#xff0c;查找是否成功生成静态库/动态库&#xff0c;一般在./bin文件中。 情况2&#xff1a; 是否在CMakeLists中target_link_libraries中添加链接静态库操作 情况3&#xff1a; 是都存在类模板&#xff0c;需要实例化&a…

eBay宣布发布全新的购买和销售APIs

eBay最近宣布发布两款全新的购买和销售APIs。这些APIs旨在促进eBay产品在第三方应用程序中的更好集成。eBay于10月19日在他们的博客上发表了几篇文章&#xff0c;不仅详细介绍了这些全新的购买和销售APIs提供的功能&#xff0c;而且还详细地总结了他们公司从SOAP&#xff08;简…

Sobel 边缘实现

#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include "opencv2/imgproc/imgproc.hpp" #include <iostream> using namespace cv; // 非极大值抑制实现sobel竖直细化边缘 bool SobelVerEdge(cv::Mat srcImage, cv::…

vue下实现textarea类似密码框的功能之探索input输入框keyup,keydown,input事件的触发顺序...

项目中引入element的input框组件&#xff0c;触发事件必须要加上.native <el-input placeholder"请输入" type"textarea" v-model"valueText" keyup.native"keyUp(valueText,$event)" keydown.native"keyDown($event)" …

【C++】动态内存管理/move/以及移动构造与移动赋值运算符

文章目录1 .对象移动与右值引用 实际应用过程中遇到的问题及其解决方案c中临时变量不能作为非const的引用参数2. 动态内存管理类3. 对象移动与右值引用4. 移动构造与移动复制运算符1 .对象移动与右值引用 实际应用过程中遇到的问题及其解决方案 问题描述&#xff1a; bool Cr…

图像直接卷积 Sobel 边缘实现

bool sobelEdge(const cv::Mat& srcImage, cv::Mat& resultImage,uchar threshold) {CV_Assert(srcImage.channels() 1);// 初始化水平核因子Mat sobelx (Mat_<double>(3, 3) << 1, 0,-1, 2, 0, -2, 1, 0, -1);// 初始化垂直核因子Mat sobely (Mat_&…

JSON.parse解析特殊字符报错解决方案

2019独角兽企业重金招聘Python工程师标准>>> 具体案例&#xff1a; 页面点击”下一任务” 会去请求后台&#xff0c;这里出现的问题是有虚拟任务的时候。然后会返回一个map&#xff0c;也就是如下图中回调函数中的data。 当该map里存有以下字符的时候&#xff1a; \…

MySQL数据库字符集和整理

MySQL数据库字符集和整理(2009-11-20 22:23:37)mysql数据库 it 其实这个表在MySQL数据库中通过phpMyAdmin就能看到&#xff0c;icech只是把表格整理了一下方便大家使用&#xff0c;如果要更换数据库的字符集&#xff0c;心里有数。其中有三种utf8_general_ci、utf8_unicode_ci…

【SLAM后端】—— ceres优化相机位姿求解

求解结果如下&#xff1a; mat 初始化&#xff0c;eigenvalue初始化 Mat K ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );Eigen::Matrix<float,3,1> vd_3d;v_3d << 3, 2, 1;求解目标函数结构体构造与实例 struct CurveFi…

SPOJ 1811 LCS [后缀自动机]

题意&#xff1a; 求两个串的最大连续子串 一个串建SAM&#xff0c;另一个串在上面跑 注意如果走了Suffix Link&#xff0c;sum需要更新为t[u].val1 Suffix Link有点像失配吧&#xff0c;当前状态s走不了了就到Suffix Link指向的状态fa上去&#xff0c;fa是s的后缀所以是可行的…

图像卷积下非极大值抑制 Sobel 的实现

bool sobelOptaEdge(const cv::Mat& srcImage, cv::Mat& resultImage, int flag) {CV_Assert(srcImage.channels() 1);// 初始化sobel水平核因子cv::Mat sobelX (cv::Mat_<double>(3, 3) << 1, 0, -1,2, 0, -2, 1, 0, -1);// 初始化sebel垂直核因子cv::…

was unable to refresh its cache! status = Cannot execute request on any known server

出现这种错误是因为: Eureka服务注册中心也会将自己作为客户端来尝试注册它自己&#xff0c;所以我们需要禁用它的客户端注册行为。 在 yml中设置 eureka.client.register-with-eurekafalse eureka.client.fetch-registryfalse 但在服务端是要这是为false的&#xff0c;在客…

【C++】浅析析构函数(基类中)为什么要写成虚基类?

为什么有了虚析构函数&#xff0c;就能先调用子类的析构函数&#xff1f; class A {virtual ~A(){} };class B : A {virtual ~B(){} };A *p new B(); delete p; 唯一差别是&#xff0c;每个析构函数结束时会自动&#xff08;隐含地&#xff09;调上父类的析构函数&#xff0…

Roberts 边缘检测

#include <opencv2/opencv.hpp> // roberts算子实现 cv::Mat roberts(cv::Mat srcImage) {cv::Mat dstImage srcImage.clone();int nRows dstImage.rows;int nCols dstImage.cols;for (int i 0; i < nRows - 1; i){for (int j 0; j < nCols - 1; j){// 根据公…

vector、map删除当前记录

map<string, string> sMap; map<string, string>::iterator iter; for(iter sMap.begin();iter ! sMap.end();/* iter */) {sMap.erase(iter); }注意下列错误表达&#xff1a;1. for(iter sMap.begin();iter ! sMap.end(); iter ) {sMap.erase(iter); } 错误原因…

1-2 postman工具简介

postman提供了一个多窗口和多选项卡页面用于发送和接受请求&#xff0c;postman努力保持整洁和灵活&#xff0c;提供更多的空间&#xff0c;满足用户的需要。他很简单&#xff0c;能满足大部分接口的测试&#xff0c;性价比特别高。如图所示&#xff1a; 1.侧边栏 postman的侧边…

【C++】重载运算符(一)

1.1 重载运算符特点 重载运算符本质上是一次函数调用 除了operator() 运算符调用外&#xff0c;其他重载运算符不能含有默认参数。 当重载的运算符是成员函数时&#xff0c;this绑定到左侧运算对象。成员运算符函数&#xff08;显式&#xff09;的参数数量比运算对象少一个。…

javaScript的调试(二)

2019独角兽企业重金招聘Python工程师标准>>> 一、Firebug Firebug是Firefox浏览器的调试工具&#xff0c;只要我们在Firefox中安装了Firebug应用&#xff0c;就可以按F12或右击鼠标开启调试 那么我们就先来看一下如何在Firefox中安装了Firebug应用&#xff0c;一图剩…

Prewitt 边缘检测

#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include "opencv2/imgproc/imgproc.hpp" #include <iostream> // prewitt算子实现 cv::Mat prewitts(cv::Mat img, bool verFlag false) {img.convertTo(img, CV_32FC…

[swift 进阶]读书笔记-第十一章:互用性 C11P1 实践:封装 CommonMark

第十一章&#xff1a;互用性 Interoperability 前言&#xff1a; swift 的最大优点就是与C 或者 OC 混编的时候稳的一匹 本章主要讲了swift和C之间的一些知识点。 11.1 实践:封装 CommonMark Hands-On: Wrapping CommonMark 这一小节更像是一个教程。教你如何封装C语言中的Comm…

【Cmake】Cmake学习记录

Cmake学习记录 1.1 常例 add_library(gen_reference_infogen_reference_info/gen_reference_info.hgen_reference_info/gen_reference_info.cc ) target_link_libraries(gen_reference_infoparamsimage_preprocessorcenter_extractorremapperdescriptor_generator${OpenCV_LI…

MySQL(三)用正则表达式搜索

正则表达式是用来匹配文本的特殊的串&#xff08;字符集合&#xff09;&#xff0c;将一个模式&#xff08;正则表达式&#xff09;与一个文本串进行比较&#xff1b; 所有种类的程序设计语言、文本编辑器、操作系统等都支持正则表达式&#xff0c;正则表达式用正则表达式语言来…

计算梯度幅值与方向

Mat magX Mat(src.rows, src.cols, CV_32F); Mat magY Mat(src.rows, src.cols, CV_32F); Sobel(image, magX, CV_32F, 1, 0, 3); Sobel(image, magY, CV_32F, 0, 1, 3); // 计算斜率 Mat slopes Mat(image.rows, image.cols, CV_32F); divide(magY, magX, slopes); // 计…

好程序员web前端技术分享媒体查询

为什么80%的码农都做不了架构师&#xff1f;>>> 好程序员web前端技术分享媒体查询 什么是媒体查询 媒体查询可以让我们根据设备显示器的特性&#xff08;如视口宽度、屏幕比例、设备方向&#xff1a;横向或纵向&#xff09;为其设定CSS样式&#xff0c;媒体查询由媒…

【C++】重载运算符(二)

1.4 下标运算符p501 下标运算符必须是成员函数&#xff0c;表示容器的类通常可以通过容器中的位置访问元素&#xff0c;定义下标运算符operator[]一个包含下标运算符的类&#xff0c;通常&#xff0c;定义2个版本&#xff1a;一个返回普通引用&#xff0c;另一个是类的常量成员…

手动建库11.2.0.4

环境&#xff1a; oracle11.2.0.4 redhat6.2 在上篇文章中&#xff0c;我们只安装了oracle&#xff0c;还没有建立实例&#xff0c;本篇文章就来介绍如果手动建立实例。 1.创建密码文件&#xff08;password file&#xff09;----非必要 cd $ORACLE_HOME/dbs/ 查看是否有init.o…