每次都能让人头大的 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 方式 ...
随机推荐
- CUDA -- 规约求矩阵的行和
求矩阵每行的和? 可以把每行放入一个不同线程块,这样行与行之间进行粗粒度的并行.而对于每行,其对应的线程块中分配n个线程(对应行宽),使用共享存储器,让每个线程从显存中读取一个数至shared mem ...
- Ambari 大数据集群管理
最近做了一个大数据项目,研究了下集群的搭建,现在将集群搭建整理的资料与大家分享一下!如有疑问可在评论区回复. 1前置配置 Centos7系统,每台系统都有java运行环境 全程使用root用户,避免安 ...
- 【bzoj3601】一个人的数论(莫比乌斯反演+拉格朗日插值)
传送门 题意: 求\[ \sum_{i=1}^{n}i^d[gcd(i,n)=1] \] 思路: 我们对上面的式子进行变换,有: \[ \begin{aligned} &\sum_{i=1}^ ...
- day53_9_17 django数据库表关联,路由和视图
一.数据库的关系建立. 在原生的数据库语句中,建立表与表之间的联系,就是添加一个字段,将联系的表的id值添加到该字段中. django所作的也就是这些. 以图书管理系统为例,图书管理系统有四张表:书籍 ...
- [C5] Andrew Ng - Structuring Machine Learning Projects
About this Course You will learn how to build a successful machine learning project. If you aspire t ...
- Paper | FFDNet: Toward a Fast and Flexible Solution for CNN based Image Denoising
目录 故事背景 核心思想 FFDNet 网络设置 噪声水平图 对子图像的去噪 保证噪声水平图的有效性 如何盲处理 为啥不用短连接 裁剪像素范围 实验 关于噪声水平图的敏感性 盲处理 发表在2018 T ...
- Ansible 如何只输出错误信息?
有时主机较多时,我们只想关注有问题的主机. Ansible callback 插件中有一个 actionable,官方描述为: actionable - shows only items that n ...
- 使用puppeteer爬取网页数据实践小结
简单介绍Puppeteer Puppeteer是一个Node库,它通过DevTools协议提供高级API来控制Chrome或Chromium.Puppeteer默认以无头方式运行,但可以配置为有头方式 ...
- 02Shell变量
Shell变量 什么是 shell 变量 shell变量就是 用一个固定的字符串去表示不固定的内容 变量的类型 自定义变量 定义变量 变量名=变量值 (显式赋值) 变量名必须以字母或下划线开头,区分大 ...
- D3力布图绘制--节点跑掉,单曲线弯曲问题记录
D3力布图绘制中遇到的交互问题,频繁操作数据后,会出现节点跑掉和单曲线弯曲的问题 问题描述 在id指向都正常的情况下出现以下2种状况: 单曲线弯曲 节点跑掉 经排查,是数据重复导致的问题 线条也是一样 ...
