当前位置: 代码迷 >> 综合 >> unity shader 之基础 七八 纹理采样、透明度渲染
  详细解决方案

unity shader 之基础 七八 纹理采样、透明度渲染

热度:35   发布时间:2023-11-21 16:13:19.0

7.1.1 逐纹素:对纹理贴图进行采样,采样后的结果就叫纹素

7.1.2 unity使用的是OpenGL的标准,即:左下角是坐标原点

7.1.3 _MainTex_ST:表示该纹理的偏移缩放属性,在属性面板上表现出tilling 和 offset ,float4 类型

xy 存的是缩放  zw 存的是偏移

7.1.4 通过 _MainTex_ST 重新计算uv  texcord *_MainTex_ST.xy+_MainTex_ST.zw

      或者使用内置方法 TRANSFORM_TEX(uv,_MainTex)

7.1.5 tex2D(_MainTex,uv) 对_MainTex采样,得到该uv 坐标下的颜色

7.1.6 texture 的filtermode

filtermode: 作用是当纹理发生变换时,比如缩小或者放大,对纹理进行不同的滤波处理,得到最后的纹理质量也是不一样的,比如你一张256的纹理要放到512的模型上,纹理就要被拉伸,纹理上的一个像素要对应多个像素,图像就会变得模糊,如果不进行处理

filtermode有三种:

一:point:只对单个像素进行采样,比如在纹理缩小的时候,纹理中的多个像素对应屏幕上的一个像素,这时point 只对单个像素进行采样,得到最终屏幕上的图像,这样像素之间形成的色差比较明显,也就是卡通画效果

二:Billinear: 对周围的四个像素进行采样,然后进行线性插值得到最后的效果,看起来会有点模糊

三:Trillinear:如果使用了mipmap,还会在多张纹理之间进行混合,如果不使用mipmap,得到的想过和Billinear 效果一样

7.1.7 图片的大小长宽要设置成2的整数幂,比如 256*512,指的是texture,而不是sprite,在unity中如果你的图片大小不是2的整数幂,它提供了3个选项帮你转换成2的整数幂

7.2 凹凸纹理

7.2.1 凹凸纹理的本质是通过一张存储了顶点法线坐标的图片,营造出一种凹凸的效果,模型本身并没有改变

为什么改变了法线就能营造出凹凸的结果,因为法线的作用就是就算光照,现实生活中,你看到的物体觉得很立体,就是因为光照到物体上产生了阴影,有明暗区分,所以觉得很立体,凹凸贴图通过改变法线,间接改变了光照的方向,和阴影产生的结果,让你看起来有种立体的感觉

总之: 法线就是为了计算光照产生的

7.2.2 凹凸纹理的种类

凹凸纹理 分为两种,一种是高度贴图,一种是法线贴图

高度贴图,存储的并不是顶点的法线,而是强度值,表示该像素的海拔高度,颜色越深,表示越往里凹,因为越凹越看不见

法线并不是直接得到,而是由像素的灰度值来计算得到的,所以相比较法线贴图来说,要进行的计算更多

法线贴图,存储的就是顶点的法线坐标,比较直接

法线的范围在[-1,1],而贴图中像素的uv坐标在[0,1],所以我们采样后得到的uv坐标,要进行转换才能得到最初的法线坐标

即:normal=pixel*2-1

为什么法线贴图大部分都是浅蓝色呢?

浅蓝色表示改法线贴图存储的是切线空间下的法线,顶点的原始法线是(0,0,1),映射到像素上RGB(0.5,0.5,1),浅蓝色

法线贴图有两种形式,一种是模型空间下的法线坐标,一种是切线空间下的法线坐标

模型空间法线的优点:直接就能获取到模型的法线,不需要空间转换,因为所有的法线都是在同一坐标空间下,所以边角处通过线性插值得到的效果更加平滑

切线空间下法线的优点:

自由度更高,模型空间存储的是绝对法线,只适用于创建该法线的的模型,如果换个模型就得不到正确的结果

可以进行uv 动画,改变uv,间接改变了法线,间接改变了光照,产生了阴影变化,实现凹凸动画

可以纹理压缩,切线空间下的法线都是正方向,所以可以只存储法线xy坐标的值,z坐标=sqrt(1-max(0,dot(xy,xy)))

纹理可以重用,因为是相对的

7.2.3使用切线空间下的法线纹理

要计算光照,就要统一坐标空间,要么在切线空间,要么在世界空间,或者在其它空间下,用的最多的是前两者

切线空间,要把光照方向,视角方向 转换到切线空间,为什么是这两种,因为我们计算光照,就要用到光照模型,从而用到BRDF,也就是I,V

在切线空间下计算,上面的转换可以在顶点着色器中实现,减少计算

世界空间,把法线转换到世界空间下,因为有时候我们需要用到世界空间下的法线坐标和光照方向

比如对cubemap 进行环境采样,因为纹理坐标是逐像素的,所以要在片元着色器中进行采样然后进行一个矩阵变换,比上面多了一次矩阵运算

7.2.4 切线空间下的运算

从模型空间->切线空间 的矩阵=切线空间->模型空间的逆矩阵

切线空间->模型空间的矩阵是 切线 副切线 法线的列排列,根据第四章的知识,我们知道首先要得到切下空间下的坐标在模型空间下的坐标表示,也就是我们要分别得到 模型空间下的 切线 副切线 法线的表示,切线,法线可以直接得到 副切线叉乘就好了

如果一个矩阵之后平移和旋转,则其逆矩阵=其转置矩阵,也就是按行排列

模型空间->切线空间 的矩阵= 切线 副切线 法线的行排列

tangentNormal.xy=UnpackNormal(对法线纹理采样后的normal),如果把法线贴图标记成了normalmap,unity 会根据不同的平台解压缩,一种是经过压缩的法线贴图,只保存了其中的xy分量,z 通过它俩得到,一种是没有经过压缩的法线贴图,按照下列公式计算得到

TANGENT_SPACE_ROTATION: 得到从模型空间-》切线空间的旋转矩阵,会自动定义一个rotation的变量,这个就是旋转矩阵

如果没有标记,则需要把像素映射回法线,也就是  normal=pixel*2-1

7.2.5 世界空间下的法线运算

需要把切线空间下的法线转换到世界空间,首先要构建旋转矩阵,得到世界空间下的切线、副切线、法线

注意 这里是切线-》世界 ,所以是按列排列,切线空间下的计算是从模型空间-》切线空间,是行排列,所以它能声明一个float3*3 而这里不能声明,因为unity默认的矩阵是按行排列

7.3 渐变纹理

我们之前对纹理的采样,直接就是tex2D(tex,uv),对渐变纹理的采样是通过半兰伯特公式计算出来一个坐标进行采样的

也就是uv=0.5*max(0,dot(I,N))+0.5

最终改变了光照的计算方式 diff=C*M

M=tex2D(tex,uv)

材质的wrap mode 设置为 clamp,防止 uv出现1.00001的情况,如果repeat 就会用小数部分进行采样

原来的半兰伯特计算光照的方式是 diff=C*M*max(0,dot(I,N))

M=tex2D(tex,uv),uv=vertex.uv

7.4 遮罩纹理

注意:遮罩纹理的的四个值,可以分别存储控制其它属性的值,既用变量值控制的,可以用纹理图的通道来控制,比如 r 控制高光的强度,g 控制漫反射的强度 b 控制透明度等

8 透明效果

每个像素有透明度、颜色、深度值等属性

透明效果有两种方式:一种是alpha 测试, 一种是alpha 混合

透明度测试 简单粗暴,小于某个值得像素就不渲染

透明度混合 稍微复杂点,因为关闭了深度写入,如果不关闭,比如 一个半透明的物体在一个不透明的物体前面,如果不关闭,半透明的就会覆盖透明物体,这是不对的,但是因为关闭了深度写入,对渲染顺序又有了要求

为了解决这个渲染顺序的问题:  unity 提供了渲染队列的方式

8.2 透明度测试

透明度测试的方法,如果给定值中的任何分量小于0 则不渲染

8.3 透明度混合

需要使用Blend 方法开启混合,Blend Op 来定义混合的操作,如果不设置,默认是相加

8.4 解决复杂模型,透明度错乱的效果

之前说的是 切分模型,但是对于一些复杂的模型就变得不现实

使用两个pass,第一个pass 正常的深度写入,第二个pass 进行透明度混合,因为第一个pass中已经获得了逐像素的深度关系,这样就解决了,模型本身穿插的问题

问题还是没有办法多呀

ColorMask 0 表示不显示任何颜色

 8.5 颜色混合(Blend)

blend 里面的源颜色(source color)是该pass 产生的颜色, 目标颜色(destination color),就是颜色缓存里的颜色

BlendOp

注意 Min 和 Max 是逐分量比较的

常见的混合类型:

8.7 cull 踢除指令

Cull Back 不渲染背面

  相关解决方案