之前也说过引擎能不能提供一个一般化的开发环境给使用者, 这样使用者只需要指定他要的开发环境, 就能用它最熟悉的方式去写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. 201871010113-刘兴瑞《面向对象程序设计(java)》第六-七周学习总结

    项目 内容 这个作业属于哪个课程 <任课教师博客主页链接> https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 <作业链接地址>htt ...

  2. sudo apt-get 与 yum 常用命令

    yum -RedHat:CentOS... -xxx.rpmsudo apt-get  -Debian:Ubuntu...   -xxx.deb 安装工具rpm -ivh yum-2.0.4-1.rh ...

  3. redis数据查看工具

    Redis缓存数据库目前已大量的应用,广泛用于存储session信息,权限信息,交易作业等热数据.但是Redis存在的数据可视化不便.Redis的数据查看维护困难.Redis状态监控运维不易等问题.使 ...

  4. 17. 抽象建模能力&发散思维能力&综合(5)

    抽象建模能力 题一:[扑克牌顺子] LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽 ...

  5. Introduction to Semidefinite Programming (SDP)

    https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-251j-introduction-to-mathe ...

  6. 重构 改善既有代码的设计 (Martin Fowler 著)

    第1章 重构, 第一个案例 1.1 起点 1.2 重构的第一步 1.3 分解并重组 statement() 1.4 运用多态取代与价格相关的条件逻辑 1.5 结语 第2章 重构原则 2.1 何谓重构 ...

  7. 在Azure DevOps Server 中提交Maven 依赖包(mvn deploy-file)

    Contents 1. 概述 2. 必要准备 安装Java 下载安装Maven 3. 服务器配置 新建连接源 4. 客户端配置 5. 上传maven包文件 6. 常见问题 Maven最新版本3.6.2 ...

  8. golang实战--家庭收支记账软件(面向过程)

    1.开发流程 2.目标 模拟实现一个基于文本界面的(家庭记账软件) : 初步掌握编程技巧和调试技巧: 主要包含以下知识点:局部变量和基本数据类型.循环语句.分支语句.简单屏幕格式输出.面向对象编程: ...

  9. python-4-格式化输出

    前言 有些小伙伴在打印中乱码或者编码不对,在这里讲格式化输出前,先讲下编码.我们都知道目前主流使用就是utf-8编码. 一.编码简介 编码用来让计算机识别,当然我们都知道计算机只能识别01010101 ...

  10. paramiko简介

    一.什么是paramiko 要想明白什么是paramiko,要先明白ssh协议. 二.什么是ssh协议 ssh全称是Secure Shell (翻译:安全的外壳),根据字面意思就可以知道是和安全相关的 ...