游戏开发论坛

 找回密码
 立即注册
搜索
查看: 2258|回复: 0

Shader从入门到跑路:颜色自定义输出、纹理采样

[复制链接]

1万

主题

1万

帖子

3万

积分

论坛元老

Rank: 8Rank: 8

积分
36572
发表于 2020-3-27 13:43:06 | 显示全部楼层 |阅读模式
v2-0802c4f31a3f6733eae6ea71e252a609_1200x500.jpg


前言

我们来加深一下目前理解到的内容,并且着手解决一些在学习过程中遇到的疑惑(如果有疑惑但我没提到欢迎在评论留下我会随后加上)。

首先,以往我们写程序是在CPU上跑,但写shader则是在GPU上跑,我们会用两对好兄弟,分别是vertex shader和fragment shader。

vertex shader是每个顶点都会调用一次的程序,它可以访问顶点的位置、法线、纹理等信息,这些值在经过一些处理之后,会先跑一个叫做“光栅化(Rasterization)”的过程。我们都知道,显示器是由红绿蓝 LED 组成的显示单元,因此单元之间是离散的,而“连续”的三维图像要渲染到屏幕上,就要经过一系列的修改,称之为光栅化。

此时得到就是片元(fragment),我之前说过片元与最终的图像单位是非常接近的(我甚至鼓励大家以图形的方式来理解它们实在是抱歉),但它们之间仍存在着微妙的差别。这是因为片元需要先通过一些测试,比如深度测试、透明度测试等,以决定它们是需要被使用还是被抛弃。其实在这里并不需要太纠结片元到底是不是像素,因为这不是这个系列的重点。

而由每个片元分别调用一次的就是fragment shader,它可以获取片元的二维、深度、颜色等信息。注意,fragment shader获取的片元颜色是根据片元的三个顶点颜色插值计算后得到的,也就是说如果三个顶点颜色并不相同,那么最终颜色便是渐变的。

shader的计算是在CGPROGRAM内完成的,并且应该符合以下结构

  1. struct appdata{};  // 获取模型数据
  2. struct v2f{};  // 存放计算结果
  3. v2f vert(appdata v){}  // 拿取模型数据计算后传给fragment shader
  4. float4 frag(v2f i) : SV_Target{}  // 拿取计算结果作用于目标
复制代码

注意,在结构体内定义的输入输出是由语义限定的,举个栗子:

  1. struct appdata{
  2.      float4 vertex : POSITION;  // 把模型顶点坐标填充到vertex中
  3.      float2 uv : TEXCOORD1;  // 第一组纹理坐标
  4.      float3 normal : NORMAL;  // 把法线方向填充到normal中
  5. };
  6. struct v2f{
  7.      float4 vertex : SV_POSITION;  // 输出到裁剪空间中的顶点坐标
  8.      float3 color : COLOR; // 输出颜色
  9. };
  10. float4 frag(v2f i) : SV_Target{ // frag输出值直接用于渲染
  11.      return float4(i.color, 1.0);  
  12. }
复制代码

语义是很聪明的一种设计,它能够让Shader知道从哪里获取数据,并且还知道把输出的数据放到哪里(或者怎么放)

问题1:顶点位置是一种坐标系,uv也是一种坐标系,既然都是坐标为什么不能只算一个多方便啊?

好问题!(拍大腿)这两种坐标系的应用是不一样的,顶点是一个和网格(mesh)相关的局部点,以xyz为坐标轴;而uv则是贴图映射到模型表面的依据,以uvw为坐标轴,或者可以将uv看作一张纹理查找表格。因为xyz轴已经被网格占用了,所以贴图用的坐标轴就只能用uvw了,简称uv。很多同学看了一些shader教程经常见到它们两一齐出现,自己写的时候不知所以索性就一齐计算了。

2.jpg
5.4 鲁迅名言

问题2:有些shader程序的frag函数输出的COLOR,而另一些则是SV_TARGET,有什么区别啊?

没啥不同,SV开头的是Direct3D定义的一种System Value语义,SV_TARGET对应的就是COlOR,但有些平台支持SV语义,而有些则不支持。但这并不重要,因为编译器会自动帮我们做转换,它们两的功能是一样的。类似还有SV_POSITION与POSITION,SV_DEPTH与DEPTH等等。反正无脑写SV_Target就没错了(狗头)

自我小测:

在写过一些shader之后,我们已经是个shader程序员了,当然要学会自己写shader咯,下面是一些可以努力的方向,并获取图中的渲染效果(因为是读者自我测验,因此笔者不会提供答案,但如果实在想不明白可以私信笔者问思路)

练习1 : 新增一个_Color属性,并将其与主纹理的颜色相乘

3.jpg

注:颜色在属性区块内是以Color类型存在,在CGPROGRAM使用内需被预定义为float4类型

练习2 : 用第二章讲到,用uv值计算颜色值的方法,将其结果与主纹理的颜色想乘,获得下面的效果

4.jpg

练习3 : 使用两张纹理,以及第五章讲到的拖动条,当移动滑杆时在两张纹理中进行插值

5 (1).gif

练习4 : 在图片的设置面板内将图片的wrap mode设为repeat。在对纹理采样时,将uv乘2

6.jpg

练习5 : 使用明亮度(luminance)公式来算出一个明亮度值,然后将红绿蓝通道都设置为明亮度值,透明度通道则用对图片采样获得的透明度,最终获得灰度效果

7.jpg

注: luminance = 0.2125 * Red + 0.07154 * Green + 0.0721 * Blue

练习6 : 将练习5的返回颜色与自定义的颜色属性相乘

8 (1).gif


相关阅读:
Shader从入门到跑路:朴实无华的图形学基础
Shader从入门到跑路:自定义纹理输入
Shader从入门到跑路:屏幕后处理效果

Shader从入门到跑路:实作屏幕扭曲效果

作者:俊铭
专栏地址:https://zhuanlan.zhihu.com/p/86192364

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2024-4-20 01:59

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表