之前也说过引擎能不能提供一个一般化的开发环境给使用者, 这样使用者只需要指定他要的开发环境, 就能用它最熟悉的方式去写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 -- 从整合说起的更多相关文章

  1. 每次都能让人头大的 Shader -- 从一次简单的功能说起

    最近有个功能, 要渲染从主相机视角看到的另一个相机的可视范围和不可见范围, 大概如下图 : 简单来说就是主相机视野和观察者相机视野重合的地方, 能标记出观察者相机的可见和不可见, 实现原理就跟 Sha ...

  2. Toast工具类,Android中不用再每次都写烦人的Toast了

    package com.zhanggeng.contact.tools; /** * Toasttool can make you use Toast more easy ; * * @author ...

  3. 解决Mac下SourceTree每次都让输入密码的问题

    在Mac上操作sourcetree当pull和push时每次都是让输入密码,非常烦人,虽然大概知道是因为SSH什么的问题,但搜索百度也没发现解决办法. 于是乎搜索谷歌,发现如下解决办法. Source ...

  4. Istio 运维实战系列(3):让人头大的『无头服务』-下

    本系列文章将介绍用户从 Spring Cloud,Dubbo 等传统微服务框架迁移到 Istio 服务网格时的一些经验,以及在使用 Istio 过程中可能遇到的一些常见问题的解决方法. 失败的 Eur ...

  5. Git push 时每次都需要密码的疑惑

    2015.1.13更新: 在本地搭建Git服务器时,也是有每次操作需要密码的情况. 是因为每次做推送动作时,Git需要认证你是好人.所以需要密码. 可以在 /home/username/.ssh/au ...

  6. 为什么每个请求都要有用户名密码呢,那不是每次都要查询一下了,token,表示这个用户已经验证通过了,在token有效期内,只需要判断token是否有效就可以了

    为什么每个请求都要有用户名密码呢,那不是每次都要查询一下了,token,表示这个用户已经验证通过了,在token有效期内,只需要判断token是否有效就可以了

  7. LISTVIEW嵌套GRIDVIEW的一些处理(点击GRIDVIEW的条目,能够显示他在LISTVIEW中的位置)(对这篇文章的优化处理,不每次都new onItemClickListener)

    前几天写了点击GRIDVIEW的条目,能够显示他在LISTVIEW中的位置,当时的处理是在ListView的适配器里的GetView方法里每次都new GridView的onItemClickList ...

  8. git 设置不需要输入密码, 去除 fetch / pull 代码每次都需要输入密码的烦恼

    https方式每次都要输入密码,按照如下设置即可输入一次就不用再手输入密码的困扰而且又享受https带来的极速 设置记住密码(默认15分钟): git config --global credenti ...

  9. 使用git提交到github,每次都要输入用户名和密码的解决方法

    使用git提交文件到github,每次都要输入用户名和密码,操作起来很麻烦,以下方法可解决,记录以下. 原因:在clone 项目的时候,使用了 https方式,而不是ssh方式. 默认clone 方式 ...

随机推荐

  1. pdfium ppm demo

    #include "fpdfview.h" #include <iostream> #include <string> #include <strin ...

  2. c# WF 第9节 button控件

    本节内容: 1:实现实例 1:实现实例 每当点击一个确定就出现一个窗口,当点击最后的确定时,关闭所有的窗口. 实现: 步骤1:对Form 1 -Form3 依次进行如下设置: 步骤2 : 当每点击一个 ...

  3. linux/Deepin /Debian 9 Stretch安装Wine

    我们可以使用Debian stretch源在Deepin 15.9系统下安装Wine 4.0,同时也可以用在Debian 9 Stretch系统下安装Wine 4.0,方法是一样的,同时下面介绍的方法 ...

  4. C++ 标准库 std::remove

    参见:https://zh.cppreference.com/w/cpp/algorithm/remove std::remove 不会改变输入vector / string 的长度.其过程,相当于去 ...

  5. CF991C Candies

    CF991C Candies 洛谷评测传送门 题目描述 After passing a test, Vasya got himself a box of nn candies. He decided ...

  6. Hbulder 调试安卓app

    目前开发app有原生开发和web开发两种方式,各有各的优势和劣势,利用web技术开发app可以只用写一套代码,即可以在Android和ios同时应用,比较方便和快捷,有很多中不同的开发方式,例如cor ...

  7. 分布式共识算法 (四) BTF算法(区块链使用)

    系列目录 分布式共识算法 (一) 背景 分布式共识算法 (二) Paxos算法 分布式共识算法 (三) Raft算法 分布式共识算法 (四) BTF算法 一.引子 前面介绍的算法,无论是 Paxos ...

  8. 第04组 Alpha冲刺(5/6)

    队名:new game 组长博客:戳 作业博客:戳 组员情况 鲍子涵(队长) 燃尽图 过去两天完成了哪些任务 才两天,也就是实现一些功能而已 复习了一下SuffixAutomata 接下来的计划 实现 ...

  9. Java 并发系列之十一:并发线程带来的风险

    1. 概述 在并发中有两种方式,一是多进程,二是多线程,但是线程相比进程花销更小且能共享资源. 线程带来的风险: 1. 安全性问题.错误的问题永不发生.竞态条件(顺序敏感). 2. 活跃性问题.正确的 ...

  10. SpringBoot启用https

    1.利用JDK自带的keytool生成证书 keytool -genkey -storetype PKCS12 -alias tomcat -keyalg RSA -keysize 2048 -key ...