一、立方体纹理

立方体纹理是环境映射的一种实现方式,立方体纹理就是立方体的六个面,每个面有一个纹理,一般用于映射出物体周围环境。

和基础纹理不同,采样立方体纹理需要一个三维坐标,而这个三维坐标由一条向量与立方体的交点构成,注意采样时,向量是由立方体内的某一点发出,向外指的,不是立方体外的某一点指向立方体内部。

关于凹物体可能会反射自身的问题不做讨论。

Unity里的天空盒就是一种立方体纹理,将整个场景包围在一个立方体内。

1.1 创建立方体纹理:

立方体纹理主要用于环境映射,说白点就是让一个物体表面反射出周围环境的样子,但每个物体在不同地方的采样立方体纹理有差距,因此要用脚本辅助截取当前物体所处位置的立方体纹理。

脚本:

using UnityEngine;
using UnityEditor;
using System.Collections;public class RenderCubemapWizard : ScriptableWizard {public Transform renderFromPosition;public Cubemap cubemap;void OnWizardUpdate () {helpString = "Select transform to render from and cubemap to render into";isValid = (renderFromPosition != null) && (cubemap != null);}void OnWizardCreate () {GameObject go = new GameObject( "CubemapCamera");go.AddComponent<Camera>();go.transform.position = renderFromPosition.position;go.GetComponent<Camera>().RenderToCubemap(cubemap);DestroyImmediate( go );}[MenuItem("GameObject/Render into Cubemap")]static void RenderCubemap () {ScriptableWizard.DisplayWizard<RenderCubemapWizard>("Render cubemap", "Render!");}
}

通过继承窗口编辑器实现。

1.2 利用反射采样立方体纹理:

利用入射方向(说反射更合理)求采样坐标,我们知道反射方向就是视角方向,根据光路可逆可以求出立方体纹理的哪个点发出的光线进入人眼,而这个点纹理颜色,就是这个片元或者说顶点的颜色,但注意采样时的向量是向外发散的,如果你计算的时候没有对视角方向取反,那么就需要对入射方向取反(取决于你在那个阶段计算),入射方向无需单位化 。

Shader "Custom/Test0"
{Properties{_Color("Color",Color)=(1,1,1,1)_CubemapColor("立方体纹理颜色",Color)=(1,1,1,1)//越强越能看出周围环境_ReflectLevel("立方体纹理反射水平",Range(0,1))=1_Cubemap("立方体纹理",Cube)="_Skybox"{}}SubShader{Pass{Tags{"LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "UnityLightingCommon.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;samplerCUBE _Cubemap;fixed4 _CubemapColor;fixed _ReflectLevel;fixed _AlphaBlend;struct a2v{float4 vertex:POSITION;float3 normal:NORMAL;                };struct v2f{float4 pos:SV_POSITION;float4 worldPos:TEXCOORD0;float3 worldNormal :TEXCOORD1;float3 texDir:TEXCOORD2;                                    };v2f vert(a2v v){v2f o;o.pos=UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos=mul(unity_ObjectToWorld,v.vertex);float3 worldViewDir=UnityWorldSpaceViewDir(o.worldPos);o.texDir=reflect(-worldViewDir,o.worldNormal);return o;}fixed4 frag(v2f i):SV_Target{                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;fixed3 worldNormal=normalize(i.worldNormal);fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);               fixed3 refleResult=texCUBE(_Cubemap,i.texDir)*_CubemapColor;                               fixed3 diffuse=_LightColor0*_Color*(0.5*dot(worldLightDir,worldNormal)+0.5);fixed3 color=ambient+lerp(diffuse,refleResult,_ReflectLevel);return fixed4(color,1);}ENDCG}}
}

1.3 利用折射采样立方体纹理:

立方体纹理的某一点经过物体内部的折射,最终进入摄像机,过程也是求入射方向是哪个点发出的,同样我们也知道反射方向。

折射公式:

Cg函数: refract(入射方向,法线方向,);

Shader "Custom/Test0"
{Properties{_Color("Color",Color)=(1,1,1,1)_CubemapColor("立方体纹理颜色",Color)=(1,1,1,1)//越强越能看出周围环境_RefractLevel("立方体纹理折射水平",Range(0,1))=1_Cubemap("立方体纹理",Cube)="_Skybox"{}_RefractRatio("介质比值",Range(0,1))=0.5}SubShader{Pass{Tags{"LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "UnityLightingCommon.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;samplerCUBE _Cubemap;fixed4 _CubemapColor;fixed _RefractLevel;fixed _RefractRatio;struct a2v{float4 vertex:POSITION;float3 normal:NORMAL;                };struct v2f{float4 pos:SV_POSITION;float4 worldPos:TEXCOORD0;float3 worldNormal :TEXCOORD1;float3 texDir:TEXCOORD2;                                    };v2f vert(a2v v){v2f o;o.pos=UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos=mul(unity_ObjectToWorld,v.vertex);float3 worldViewDir=UnityWorldSpaceViewDir(o.worldPos);o.texDir=refract(normalize(-worldViewDir),normalize(o.worldNormal),_RefractRatio);return o;}fixed4 frag(v2f i):SV_Target{                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;fixed3 worldNormal=normalize(i.worldNormal);fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);               fixed3 refleResult=texCUBE(_Cubemap,i.texDir)*_CubemapColor;                               fixed3 diffuse=_LightColor0*_Color*(0.5*dot(worldLightDir,worldNormal)+0.5);fixed3 color=ambient+lerp(diffuse,refleResult,_RefractLevel);return fixed4(color,1);}ENDCG}}
}

1.4 利用菲涅尔反射控制立方体采样:

菲涅尔,通常指的是反射光和入射光存在一定比例,比如水面,当我们站在不同角度看,有些角度可以看到水面下,有些角度就是一片白太阳光全反射。

Shader "Custom/Test0"
{Properties{_Color("Color",Color)=(1,1,1,1)_CubemapColor("立方体纹理颜色",Color)=(1,1,1,1)//越强越能看出周围环境_FresnelScale("菲涅尔系数",Range(0,1))=1_Cubemap("立方体纹理",Cube)="_Skybox"{}}SubShader{Pass{Tags{"LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "UnityLightingCommon.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;samplerCUBE _Cubemap;fixed4 _CubemapColor;fixed _FresnelScale;        struct a2v{float4 vertex:POSITION;float3 normal:NORMAL;                };struct v2f{float4 pos:SV_POSITION;float4 worldPos:TEXCOORD0;float3 worldNormal :TEXCOORD1;float3 worldViewDir :TEXCOORD2;float3 texDir:TEXCOORD3;                                    };v2f vert(a2v v){v2f o;o.pos=UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos=mul(unity_ObjectToWorld,v.vertex);o.worldViewDir=UnityWorldSpaceViewDir(o.worldPos);o.texDir=reflect(-o.worldViewDir,o.worldNormal);return o;}fixed4 frag(v2f i):SV_Target{                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;fixed3 worldNormal=normalize(i.worldNormal);i.worldViewDir=normalize(i.worldViewDir);fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);fixed fresnel=_FresnelScale+(1-_FresnelScale)*pow(1-dot(i.worldViewDir,i.worldNormal),5);               fixed3 refleResult=texCUBE(_Cubemap,i.texDir)*_CubemapColor;                               fixed3 diffuse=_LightColor0*_Color*dot(worldLightDir,worldNormal);fixed3 color=ambient+lerp(diffuse,refleResult,saturate(fresnel));return fixed4(color,1);}ENDCG}}
}

当菲涅尔系数为0时,物体就只有边缘可以反射立方体纹理,从而实现了边缘发光。当然不是只有立方体纹理可以用边缘发光,最终Lerp插值那里可以是随便一个颜色。而且菲涅尔也不一定非要用立方体纹理这种混合方式,随便一张纹理什么的也可以。反正图形学第一准则:如果这个东西看起来是对的,那么它就是对的。

二、渲染纹理

就是Unity内置的Render Texture,即把场景渲染到一张纹理中,Unity允许我们在场景渲染中或者渲染完毕时获取当前的渲染图像用于做一些特殊操作。

创建Render Texture的方法。一个是在Assets里创建,然后用摄像机填充,另一个是在Shader中使用GrabPass命令或者脚本内的OnRenderImage函数。

在这里我们使用第一种方法,因为简单,而且适用于只需要一次抓取就能解决情况的Shader,一般OnRenderImage适用于需要对渲染纹理做出多次处理,在后面我们会遇到。其次,GrabPass多用于配合内置函数实现局部渲染纹理的抓取,而后者多用于整个图像的处理。

关于渲染纹理能做的事情,这里只举两个例子,一个是类似的镜子反射,即用一个摄像机拍出来背面,然后用一个面片当镜子显示渲染纹理。

Shader "Custom/Test0"
{Properties{_RenderTex("渲染纹理",2D)="white"{}}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"sampler2D _RenderTex;float4 _RenderTex_ST;            struct a2v{float4 vertex:POSITION;float2 texcoord:TEXCOORD0;};struct v2f{float4 pos:SV_POSITION;float2 uv:TEXCOORD3;};v2f vert(a2v v){v2f o;o.pos=UnityObjectToClipPos(v.vertex);               o.uv=TRANSFORM_TEX(v.texcoord,_RenderTex);o.uv.x=1-o.uv.x;return o;}fixed4 frag(v2f i):SV_Target{                return tex2D(_RenderTex,i.uv);                }ENDCG}}
}

这样写其实不是完整的镜子,有很大的局限性,首先镜面的显示内容受面片大小以及纹理的大小影响,你要不停的调整摄像机参数来达成镜子中比较真实的物体的大小,其次,你一旦转动视角,镜子内的画面并不会随之改变,从镜子的成像原理上也可以解释这种实现方法的不真实性。

第二个是玻璃折射效果,这个理解起来复杂一些。我们平常看玻璃,光线进入我们的眼睛中可以分为两种主要类型:一个是玻璃后面物体透过玻璃的折射光线,一个是玻璃表面的反射环境的反射光线。

反射光线好说,用立方体纹理采个样。

折射光线的模拟稍微复杂,就是利用内置函数抓取玻璃后面的图像,然后用玻璃的法线贴图给玻璃后面的图像做一个偏移或者说扰动。这里涉及渲染顺序的问题,我们要等所有不透明物体(当然,你要是想模拟透明物体的折射效果就把渲染顺序再往后设一级)渲染完毕后抓取当时的渲染纹理,然后用我们的法线贴图和采样纹理相加模拟出偏移效果。

Shader "Custom/Test0"
{Properties{_MainTex("主要纹理",2D)="white"{}_NormalTex("法线贴图",2D)="bump"{}_CubeMap("立方体纹理",Cube)="_Skybox"{}_RefractLevel("折射程度",Range(0,100))=50_FinalScale("反射与折射的混合比例",Range(0,1))=0.5}SubShader{Tags{"Queue"="Transparent" "RenderType"="Opaque"}GrabPass{"_RefractionTex"}Pass{CGPROGRAM           #pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"sampler2D _MainTex;float4 _MainTex_ST;sampler2D _NormalTex;float4 _NormalTex_ST;sampler2D _RefractionTex;float4 _RefractionTex_TexelSize;samplerCUBE _CubeMap;fixed _RefractLevel;fixed _FinalScale;struct a2v{float4 vertex:POSITION;float3 normal:NORMAL;float4 tangent:TANGENT;float2 texcoord:TEXCOORD0;};struct v2f{float4 pos:SV_POSITION;                float4 uv:TEXCOORD0;float4 scrPos:TEXCOORD1;float4 worldPos:TEXCOORD2;float3 worldNormal:TEXCOORD3;};v2f vert(a2v v){v2f o;                o.pos=UnityObjectToClipPos(v.vertex);o.worldPos=mul(unity_ObjectToWorld,v.vertex);o.worldNormal=UnityObjectToWorldNormal(v.normal);                o.uv.xy=TRANSFORM_TEX(v.texcoord,_MainTex);o.uv.zw=TRANSFORM_TEX(v.texcoord,_NormalTex);o.scrPos=ComputeGrabScreenPos(o.pos);return o;}fixed4 frag(v2f i):SV_Target{                float3 worldViewDir=normalize(UnityWorldSpaceViewDir(i.worldPos));fixed3 tangentNormal=UnpackNormal(tex2D(_NormalTex,i.uv.zw));//折射部分fixed2 offset=tangentNormal.xy*_RefractLevel*_RefractionTex_TexelSize.xy;i.scrPos.xy=offset+i.scrPos.xy;fixed3 refract=tex2D(_RefractionTex,i.scrPos.xy/i.scrPos.w);//反射部分                fixed3 reflectionDir=reflect(-worldViewDir,i.worldNormal);fixed4 texColor=tex2D(_MainTex,i.uv.xy);fixed3 reflection=texCUBE(_CubeMap,reflectionDir)*texColor;//最终结果fixed3 finalColor=reflection*(1-_FinalScale)+refract*_FinalScale;return fixed4(finalColor,1);}ENDCG}}
}

渲染纹理能实现的效果非常非常多。

关于GrabPass更加详细的解释,(32条消息) Unity Shader GrabPass 使用注意的问题_Jave.Lin的博客-CSDN博客_grab pass 与 rt的区别        总结大概就是,GrabPass{ }和GrabPass{ "Name" }的区别,前者会向多张纹理输出,彼此互不干扰,后者是只要纹理名字一样,全给渲染到给定名字的纹理中。这里就会涉及到一个叠加问题,前者的结果如果物体重合多个会产生纹理叠加现象,但后者并不会。性能方面后者会更好,而前者好像还增加了DrawCall等等什么的。

关于采样渲染纹理时使用的函数ComputeGrabScreenPos的作用,输入齐次裁剪空间下的坐标,转化为对应屏幕采样的uv坐标,这个uv坐标需要除以w分量才可以使用,Unity在计算时为了不破坏插值并未在计算时除以w分量。关于这个计算过程,网上很多博客都解释的很清楚,这里就不再赘述了。

关于另一个函数ComputeScreenPos的作用,去官网查了查,Unity的意思是尽量使用ComputeGrabScreenPos函数,因为后者会在某些情况下出现纹理翻转问题。

三、程序纹理

指计算机生成的图像一般用于创建个性图案或非常真实的自然元素,例如石子,木头等等,了解即可。

UnityShader基础(五)——进阶纹理相关推荐

  1. 计算机编程书籍-笨办法学Python 3:基础篇+进阶篇

    编辑推荐: 适读人群 :本书适合所有已经开始使用Python的技术人员,包括初级开发人员和已经升级到Python 3.6版本以上的经验丰富的Python程序员. "笨办法学"系列, ...

  2. 蓝牙基础知识进阶——Physical channel

    蓝牙基础知识进阶--Physical channel 二.物理通道 物理通道是piconet区分的标准,它是蓝牙系统结构层次中的最底层了. Q1:物理通道有哪些类型 物理通道通常可以分为四种类型: 1 ...

  3. UnityShader基础案例(三)——外发光(泛光)和内发光

    基于菲涅尔反射来判断边缘所在. Shader "Custom/Test0" {Properties{_MainColor("主颜色",Color)=(0,0,0 ...

  4. Django基础五之Django模型层(二)多表操作

    Django基础五之Django模型层(二)多表操作 一 创建模型 表和表之间的关系 一对一.多对一.多对多 # 作者表 比较常用的信息放在这个表中 class Author(models.Model ...

  5. 云之梦php免费教学视频下载_云知梦php基础+php进阶+mysql+linux+laravel全栈工程师【百度网盘】...

    云知梦php基础+php进阶+mysql+linux+laravel全栈工程师视频百度网盘下载,全套云知梦PHP全栈工程师教程一共172集,每天两小时深入讲解,注意只是讲,课下自己要练习,总和86天课 ...

  6. 1204、基础查询进阶、连接查询

    文章目录 一.基础查询进阶 1.常用函数 1.1 字符函数 1.2 数学函数 1.3 聚集函数 1.4 日期函数 1.5 数学计算的使用 + - \* / % **2.流程控制函数:查询表记录事可以加 ...

  7. Java零基础到进阶宝典!从小白到大神,金九银十面试这届斩获23K月薪

    Java语言是一种优秀的编程语言.它最大的优点就是与平台无关,在Windows9x.Windows NT.Solaris. Linux. MacOS以及其它平台上,都可以使用相同的代码."一 ...

  8. 强化学习从基础到进阶-案例与实践[3]:表格型方法:Sarsa、Qlearning;蒙特卡洛策略、时序差分等以及Qlearning项目实战

    [强化学习原理+项目专栏]必看系列:单智能体.多智能体算法原理+项目实战.相关技巧(调参.画图等.趣味项目实现.学术应用项目实现 专栏详细介绍:[强化学习原理+项目专栏]必看系列:单智能体.多智能体算 ...

  9. 简明python教程 豆瓣-福利分享:个人整理的Python书单,从基础到进阶

    原标题:福利分享:个人整理的Python书单,从基础到进阶 我挑选的一些书籍,大家可以自行到书店或是网上自己选购.也由于个人水平有限,很可能大家觉得优秀的书籍没有列出,如果大家有觉得不错的书籍,欢迎大 ...

  10. python网络爬虫权威指南 豆瓣_福利分享:个人整理的Python书单,从基础到进阶...

    原标题:福利分享:个人整理的Python书单,从基础到进阶 我挑选的一些书籍,大家可以自行到书店或是网上自己选购.也由于个人水平有限,很可能大家觉得优秀的书籍没有列出,如果大家有觉得不错的书籍,欢迎大 ...

最新文章

  1. 百度重新定义「智能屏」,瞄准10后
  2. MariaDB Spider分库分表引擎调研
  3. R语言实现描述性统计
  4. Hbase写数据,存数据,读数据的详细过程
  5. JavaScript 演练(4). 回调函数
  6. C#方法的参数 Ref Out Params 4种类型的参数
  7. win7下程序运行权限问题解决方案
  8. linux下mysql5.7创建用户_Linux下mysql5.7 创建、删除用户与授权
  9. 组装电脑配置单报价_怎么选择组装电脑?牢记这四点永不吃亏,第四点最重要...
  10. 帖子如何实现显示浏览次数_我是如何一步步的在并行编程中将lock锁次数降到最低实现无锁编程...
  11. 2020网上答题拿证书的竞赛_参赛答题拿证书—全国大学生知识竞赛
  12. 计算器计算经纬距离_经纬距离计算器下载_经纬距离计算器官方下载-太平洋下载中心...
  13. 如何为文件夹添加图标
  14. php百度网盘登录,php登陆状态百度网盘获取文件下载链接【思路|部分代码】
  15. 新浪微博自动推广专家破解版 自动点赞、评论
  16. 【U盘刷机】小米路由器变砖如何100%刷机成功
  17. ECharts: 绘制立体柱状图【圆柱体】
  18. linux双系统如何选择顺序,双系统中怎么设置Ubuntu多系统的默认启动顺序?
  19. 如何成为合格的LINUX系统管理员(ZZ)
  20. 血战上海滩寻找英雄血量地址 实现无敌效果

热门文章

  1. Athena电商系统(01)——Athena电商系统需求分析
  2. 第十一届中国云计算标准和应用大会 | 云计算国家标准及白皮书系列发布 华云数据全面参与编制
  3. 高质量作品集PPT模板合集(字体包和作品集封面原始文档)
  4. 5G生活到底啥样?一分钟带你提前体验!
  5. outlook邮箱怎么删除服务器数据,outlook邮箱怎么修改默认删除服务器邮件?
  6. javascript 原型属性(prototype 属性)与 实例属性(自身属性)
  7. 5G与垂直行业融合的路径和难题
  8. 安溪工业机器人_今天,安溪一批项目集中开(竣)工,总投资21.52亿元
  9. ‘when‘ expression must be exhaustive, add necessary ‘xxx‘, ‘null‘ branches or ‘else‘ branch instead
  10. CMD命令行 输入行太长 命令语法不正确 Windows CMD 问题解决