每次都能让人头大的 Shader -- 从整合说起
之前也说过引擎能不能提供一个一般化的开发环境给使用者, 这样使用者只需要指定他要的开发环境, 就能用它最熟悉的方式去写Shader了.
从提供者的角度来看, 因为有太多的应用场景无法确定, 所以提供无数多套的设定才能满足需求, 比如你要用来做一般Shader还是用来做后处理用, 后处理中的顶点位置在片元阶段的插值得到的就绝对不是所显示的对象的世界坐标.
从使用者的角度来说, 在经过多平台, 多版本的迭代之后, 要维护的代码量只会越来越多, 到最后简直不知道怎样去维护了, 举个例子 :
0. 变量声明
sampler2D _MainTex;
half4 _MainTex_ST;
half4 _MainTex_TexelSize; struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
}; struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
1. 初始代码
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
2. UV 有拉伸位移的代码 -- 多用于物体渲染
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
3. 我要用在VR上! -- 官方后处理效果可见
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = UnityStereoScreenSpaceUVAdjust(v.uv, _MainTex_ST);
return o;
}
4. 我要用在后处理上! -- 后处理对于屏幕起始点敏感
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < )
{
o.uv.y = - o.uv.y;
}
#endif
return o;
}
5. 后处理UV有拉伸位移! -- 我都不知道这段代码对不对了! (后处理的缩放系数始终为1, 偏移始终为0, 被引擎强制处理了)
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < )
{
o.uv.y = - o.uv.y;
}
#endif
return o;
}
6. 我要继续用到VR的后处理上! -- 我已经飘了!
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = UnityStereoScreenSpaceUVAdjust(v.uv, _MainTex_ST); #if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0.0)
o.uv.y = 1.0 - o.uv.y;
#endif
return o;
}
这些不是随便臆想出来的, 它的官方代码也是一样的, 随着版本的更迭提供了更多的目标平台, 所以官方的代码也在一直变化, 这只是单一个UV的变量问题, 其它问题比这多的多......有一份代码就要改一份, 各种各样Shader用的多的项目组估计要升天的.
像上面的UV变量, 正常来说你有 _MainTex_ST 的设定的话, 一定会用 TRANSFORM_TEX(v.uv, _MainTex); 的吧, 或者我使用VR 并且使用Single Pass模式的话, 肯定要去调用 UnityStereoScreenSpaceUVAdjust(v.uv, _MainTex_ST); 的吧, 就不能在顶点阶段传入的时候给我自动计算好吗, 每次还要开发者自己去加宏判断平台吗, 这些在编辑器下加个平台选项不就好了吗.
像 UNITY_UV_STARTS_AT_TOP 这种判断, 应该是每个屏幕后处理都要加的吧, 看看它出现的次数, 如果给使用者设定这个Shader是使用D3D或者OpenGL标准的话, 我们就能明确知道屏幕(0,0)点的位置在哪了, 习惯opengl的就从左上角开始计算, 习惯D3D的从左下角开始计算, 这些不就又能省掉了么, 太能折腾了.
(2020.03.10)
今天发现似乎平台Graphics APIs是可以选择的, 就在PlayerBuild Settings里面, 通过手动添加目标API就可以设定了:
运行时它会从上往下选择当前平台可用的API, 我们来测试一下吧, 为了减少变量, 只留目标API, 先测试一下OpenGL的:

用一个简单的后处理Shader来跑一下
using UnityEngine; public class EditorCamera : MonoBehaviour
{
public Shader shader;
private Material m_mat; void Start()
{
if(shader)
{
m_mat = new Material(shader);
}
} private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if(m_mat)
{
Graphics.Blit(source, destination, m_mat);
}
else
{
Graphics.Blit(source, destination);
}
}
}
Shader "Unlit/StandardTest"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
}; struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float t : TEXCOORD1;
}; sampler2D _MainTex;
float4 _MainTex_ST; v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
#if UNITY_UV_STARTS_AT_TOP
o.t = 1.0;
#else
o.t = 0.2;
#endif
return o;
} fixed4 frag(v2f i) : SV_Target
{
return i.t > 0.5 ? : ;
}
ENDCG
}
}
}
很简单, OpenGL屏幕是从左下角开始的, D3D从屏幕左上角开始的.
运行起来看看, 现在是OpenGL标准, 可见屏幕是黑的, 没有进到UNITY_UV_STARTS_AT_TOP宏里面 :

下来切换API标准, 使用D3D11 :

代码不用改, 直接重新运行看看, 屏幕是白色的了 :

所以这里的设置是可以进行不同API的测试的, 跟我前面说的差不多, 只是不能在运行时进行强行设置, 并且只选择一个API的话, 在不使用这个API的平台上应该是不能转换的了, 前面说的是希望它能做成IL代码一样, 把所有的API当成不同平台的底层代码, 把Shader编译成中间代码, 这样就没有"硬"转换了.
又碰到个问题, 使用Surface Shader的模板写多个pass的情况, 不能把surface框在Pass里面, 会报错:
[Parse error: syntax error, unexpected TOK_PASS, expecting TOK_SETTEXTURE or '}'......]
错误写法:
SubShader
{
Pass
{
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
......
ENDCG
}
Pass
{
CGPROGRAM
......
ENDCG
}
}
直接在原代码上添加新Pass就行了:
SubShader
{
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
......
ENDCG Pass
{
CGPROGRAM
......
ENDCG
}
}
每次都能让人头大的 Shader -- 从整合说起的更多相关文章
- 每次都能让人头大的 Shader -- 从一次简单的功能说起
最近有个功能, 要渲染从主相机视角看到的另一个相机的可视范围和不可见范围, 大概如下图 : 简单来说就是主相机视野和观察者相机视野重合的地方, 能标记出观察者相机的可见和不可见, 实现原理就跟 Sha ...
- Toast工具类,Android中不用再每次都写烦人的Toast了
package com.zhanggeng.contact.tools; /** * Toasttool can make you use Toast more easy ; * * @author ...
- 解决Mac下SourceTree每次都让输入密码的问题
在Mac上操作sourcetree当pull和push时每次都是让输入密码,非常烦人,虽然大概知道是因为SSH什么的问题,但搜索百度也没发现解决办法. 于是乎搜索谷歌,发现如下解决办法. Source ...
- Istio 运维实战系列(3):让人头大的『无头服务』-下
本系列文章将介绍用户从 Spring Cloud,Dubbo 等传统微服务框架迁移到 Istio 服务网格时的一些经验,以及在使用 Istio 过程中可能遇到的一些常见问题的解决方法. 失败的 Eur ...
- Git push 时每次都需要密码的疑惑
2015.1.13更新: 在本地搭建Git服务器时,也是有每次操作需要密码的情况. 是因为每次做推送动作时,Git需要认证你是好人.所以需要密码. 可以在 /home/username/.ssh/au ...
- 为什么每个请求都要有用户名密码呢,那不是每次都要查询一下了,token,表示这个用户已经验证通过了,在token有效期内,只需要判断token是否有效就可以了
为什么每个请求都要有用户名密码呢,那不是每次都要查询一下了,token,表示这个用户已经验证通过了,在token有效期内,只需要判断token是否有效就可以了
- LISTVIEW嵌套GRIDVIEW的一些处理(点击GRIDVIEW的条目,能够显示他在LISTVIEW中的位置)(对这篇文章的优化处理,不每次都new onItemClickListener)
前几天写了点击GRIDVIEW的条目,能够显示他在LISTVIEW中的位置,当时的处理是在ListView的适配器里的GetView方法里每次都new GridView的onItemClickList ...
- git 设置不需要输入密码, 去除 fetch / pull 代码每次都需要输入密码的烦恼
https方式每次都要输入密码,按照如下设置即可输入一次就不用再手输入密码的困扰而且又享受https带来的极速 设置记住密码(默认15分钟): git config --global credenti ...
- 使用git提交到github,每次都要输入用户名和密码的解决方法
使用git提交文件到github,每次都要输入用户名和密码,操作起来很麻烦,以下方法可解决,记录以下. 原因:在clone 项目的时候,使用了 https方式,而不是ssh方式. 默认clone 方式 ...
随机推荐
- pycharm 设置django server
- Octave教程
Windows安装Octave http://wiki.octave.org/Octave_for_Microsoft_Windows 基本操作(Basic Operations) octave:1& ...
- 实验1c语言的开发环境使用和数据类型,运算符,表达式
#include<stdio.h> int main() { printf("); ; } /*求两个整数的乘积*/ #include<stdio.h> int pr ...
- 算法八字符串转换正数(atoi)
请你来实现一个 atoi 函数,使其能将字符串转换成整数. 首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止. 当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之 ...
- Python之Flask框架项目Demo入门
Python+Flask框架项目Demo入门 本例子用到了 Flask+蓝图+Flask-Login+SQLAlchemy+WTForms+PyMySQL相关架构 Flask Web框架介绍 Flas ...
- 三、Spring注解之@Import
spring注解之@Import [1]@Import 参数value接收一个Class数组,将你传入的类以全类名作为id加入IOC容器中 比较简单,此处不做详细解释 [2]ImportSel ...
- pymysql 读取大数据内存卡死的解决方案
背景:目前表中只有5G(后期持续增长),但是其中一个字段(以下称为detail字段)存了2M(不一定2M,部分为0,平均下来就是2M),字段中存的是一个数组,数组中存N个json数据.这个字段如下: ...
- PHP高级进阶梳理
基础篇 1.深入理解计算机系统 2.现代操作系统 3.C程序设计语言 4.C语言数据结构和算法 5.Unix环境高级编程 6.TCP/IP网络通信详解 7.Java面向对象编程 8.Java编程思想 ...
- Sitecore 8.2 渠道简介
渠道是联系人通过广告系列或面对面与您的品牌互动时所使用的路径.联系人可以通过手机上的应用与您的品牌互动,点击社交网络上的广告访问您的网站,或访问实体店购买商品.使用Sitecore体验平台,您可以使用 ...
- SATA接口、PCI/PCIe、NVMe的介绍
SATA接口.PCI/PCIe.NVMe的介绍 SATA接口 SATA是Serial ATA的缩写,即串行ATA. SATA已经完全取代旧式PATA(Parallel ATA或旧称IDE)接口的旧式硬 ...
