PBR工作流
目标是让substance效果和unity效果一致
分2步:
1.完成1个shader,效果和standard完全一致,抛去不需要的功能
2.使用新的shader,在substance里替代原有的渲染
# pbr shader
拷贝2份完全一致的工程,都使用standard,效果调到一致
standard最大麻烦是一堆keyword,需要知道走哪个分支
外部设置进来的keyword
在frag里
Shader "PBR"
{
Properties
{
_Color("Color", Color) = (,,,)
_MainTex("Albedo", 2D) = "white" {} _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
_GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
[Enum(Metallic Alpha,,Albedo Alpha,)] _SmoothnessTextureChannel ("Smoothness texture channel", Float) = [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
_MetallicGlossMap("Metallic", 2D) = "white" {} [ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
[ToggleOff] _GlossyReflections("Glossy Reflections", Float) = 1.0 _BumpScale("Scale", Float) = 1.0
_BumpMap("Normal Map", 2D) = "bump" {} _Parallax ("Height Scale", Range (0.005, 0.08)) = 0.02
_ParallaxMap ("Height Map", 2D) = "black" {} _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
_OcclusionMap("Occlusion", 2D) = "white" {} _EmissionColor("Color", Color) = (,,)
_EmissionMap("Emission", 2D) = "white" {} _DetailMask("Detail Mask", 2D) = "white" {} _DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}
_DetailNormalMapScale("Scale", Float) = 1.0
_DetailNormalMap("Normal Map", 2D) = "bump" {} [Enum(UV0,,UV1,)] _UVSec ("UV Set for secondary textures", Float) = // Blending state
[HideInInspector] _Mode ("__mode", Float) = 0.0
[HideInInspector] _SrcBlend ("__src", Float) = 1.0
[HideInInspector] _DstBlend ("__dst", Float) = 0.0
[HideInInspector] _ZWrite ("__zw", Float) = 1.0
} SubShader
{ Tags { "LightMode" = "ForwardBase" "RenderType"="Opaque" "PerformanceChecks"="False" }
LOD pass{ CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "UnityStandardInput.cginc" struct vertinput
{
float4 vertex : POSITION;
half3 normal : NORMAL;
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
half4 tangent : TANGENT;
}; struct v2f
{
UNITY_POSITION(pos);
float4 tex : TEXCOORD0;
float3 eyeVec : TEXCOORD1;
float4 tangentToWorldAndPackedData[] : TEXCOORD2; // [3x3:tangentToWorld | 1x3:viewDirForParallax or worldPos]
half4 ambientOrLightmapUV : TEXCOORD5; // SH or Lightmap UV
UNITY_SHADOW_COORDS()
UNITY_FOG_COORDS() // next ones would not fit into SM2.0 limits, but they are always for SM3.0+
#if UNITY_REQUIRE_FRAG_WORLDPOS && !UNITY_PACK_WORLDPOS_WITH_TANGENT
float3 posWorld : TEXCOORD8;
#endif
}; inline half4 VertexGIForward(vertinput v, float3 posWorld, half3 normalWorld)
{
half4 ambientOrLightmapUV = ;
//ambientOrLightmapUV.rgb = ShadeSHPerVertex (normalWorld, ambientOrLightmapUV.rgb);
ambientOrLightmapUV.rgb += SHEvalLinearL2 (half4(normalWorld, 1.0));
return ambientOrLightmapUV;
} struct FragmentCommonData
{
half3 diffColor, specColor;
// Note: smoothness & oneMinusReflectivity for optimization purposes, mostly for DX9 SM2.0 level.
// Most of the math is being done on these (1-x) values, and that saves a few precious ALU slots.
half oneMinusReflectivity, smoothness;
float3 normalWorld;
float3 eyeVec;
half alpha;
float3 posWorld;
}; inline FragmentCommonData MetallicSetup (float4 i_tex)
{
half2 metallicGloss = tex2D(_MetallicGlossMap, i_tex.xy).ra;
metallicGloss.g *= _GlossMapScale; half metallic = metallicGloss.x;
half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m. half oneMinusReflectivity;
half3 specColor;
half3 diffColor = DiffuseAndSpecularFromMetallic (Albedo(i_tex), metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity); FragmentCommonData o = (FragmentCommonData);
o.diffColor = diffColor;
o.specColor = specColor;
o.oneMinusReflectivity = oneMinusReflectivity;
o.smoothness = smoothness;
return o;
} UnityLight MainLight ()
{
UnityLight l; l.color = _LightColor0.rgb;
l.dir = _WorldSpaceLightPos0.xyz;
return l;
} half3 NormalInTangentSpace(float4 texcoords)
{
half3 normalTangent = UnpackScaleNormal(tex2D (_BumpMap, texcoords.xy), _BumpScale);
return normalTangent;
} float3 PerPixelWorldNormal(float4 i_tex, float4 tangentToWorld[])
{
half3 tangent = tangentToWorld[].xyz;
half3 binormal = tangentToWorld[].xyz;
half3 normal = tangentToWorld[].xyz;
half3 normalTangent = NormalInTangentSpace(i_tex);
float3 normalWorld = normalize(tangent * normalTangent.x + binormal * normalTangent.y + normal * normalTangent.z); // @TODO: see if we can squeeze this normalize on SM2.0 as well
return normalWorld;
} inline FragmentCommonData FragmentSetup (inout float4 i_tex, float3 i_eyeVec, half3 i_viewDirForParallax, float4 tangentToWorld[], float3 i_posWorld)
{
i_tex = Parallax(i_tex, i_viewDirForParallax);
half alpha = Alpha(i_tex.xy);
FragmentCommonData o = MetallicSetup (i_tex);
o.normalWorld = PerPixelWorldNormal(i_tex, tangentToWorld);
o.eyeVec = normalize(i_eyeVec);
o.posWorld = i_posWorld; // NOTE: shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
o.diffColor = PreMultiplyAlpha (o.diffColor, alpha, o.oneMinusReflectivity, /*out*/ o.alpha);
return o;
} #ifdef _PARALLAXMAP
#define IN_VIEWDIR4PARALLAX(i) NormalizePerPixelNormal(half3(i.tangentToWorldAndPackedData[0].w,i.tangentToWorldAndPackedData[1].w,i.tangentToWorldAndPackedData[2].w))
#define IN_VIEWDIR4PARALLAX_FWDADD(i) NormalizePerPixelNormal(i.viewDirForParallax.xyz)
#else
#define IN_VIEWDIR4PARALLAX(i) half3(0,0,0)
#define IN_VIEWDIR4PARALLAX_FWDADD(i) half3(0,0,0)
#endif #if UNITY_REQUIRE_FRAG_WORLDPOS
#if UNITY_PACK_WORLDPOS_WITH_TANGENT
#define IN_WORLDPOS(i) half3(i.tangentToWorldAndPackedData[0].w,i.tangentToWorldAndPackedData[1].w,i.tangentToWorldAndPackedData[2].w)
#else
#define IN_WORLDPOS(i) i.posWorld
#endif
#define IN_WORLDPOS_FWDADD(i) i.posWorld
#else
#define IN_WORLDPOS(i) half3(0,0,0)
#define IN_WORLDPOS_FWDADD(i) half3(0,0,0)
#endif
#define FRAGMENT_SETUP(x) FragmentCommonData x = \
FragmentSetup(i.tex, i.eyeVec, IN_VIEWDIR4PARALLAX(i), i.tangentToWorldAndPackedData, IN_WORLDPOS(i)); inline UnityGI UnityGIBase(UnityGIInput data, half occlusion, half3 normalWorld)
{
UnityGI o_gi;
ResetUnityGI(o_gi);
o_gi.light = data.light;
o_gi.light.color *= data.atten; half3 ambient_contrib = SHEvalLinearL0L1 (half4(normalWorld, 1.0));
o_gi.indirect.diffuse = max(half3(, , ), data.ambient+ambient_contrib);
o_gi.indirect.diffuse *= occlusion;
return o_gi;
} inline UnityGI FragmentGI (FragmentCommonData s, half occlusion, half4 i_ambientOrLightmapUV, half atten, UnityLight light, bool reflections)
{
UnityGIInput d;
d.light = light;
d.worldPos = s.posWorld;
d.worldViewDir = -s.eyeVec;
d.atten = atten;
d.ambient = i_ambientOrLightmapUV.rgb;
d.lightmapUV = ; d.probeHDR[] = unity_SpecCube0_HDR;
d.probeHDR[] = unity_SpecCube1_HDR;
#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
d.boxMin[] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
#endif
#ifdef UNITY_SPECCUBE_BOX_PROJECTION
d.boxMax[] = unity_SpecCube0_BoxMax;
d.probePosition[] = unity_SpecCube0_ProbePosition;
d.boxMax[] = unity_SpecCube1_BoxMax;
d.boxMin[] = unity_SpecCube1_BoxMin;
d.probePosition[] = unity_SpecCube1_ProbePosition;
#endif
Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(s.smoothness, -s.eyeVec, s.normalWorld, s.specColor); UnityGI o_gi = UnityGIBase(d, occlusion, s.normalWorld);
//o_gi.indirect.specular = UnityGI_IndirectSpecular(d, occlusion, g);
o_gi.indirect.specular = unity_IndirectSpecColor.rgb*occlusion;
return o_gi;
} inline UnityGI FragmentGI (FragmentCommonData s, half occlusion, half4 i_ambientOrLightmapUV, half atten, UnityLight light)
{
return FragmentGI(s, occlusion, i_ambientOrLightmapUV, atten, light, true);
} v2f vert (vertinput v)
{
v2f o;
float4 posWorld = mul(unity_ObjectToWorld, v.vertex);
o.tangentToWorldAndPackedData[].w = posWorld.x;
o.tangentToWorldAndPackedData[].w = posWorld.y;
o.tangentToWorldAndPackedData[].w = posWorld.z;
o.pos = UnityObjectToClipPos(v.vertex);
o.tex.xy = TRANSFORM_TEX(v.uv0, _MainTex);
o.eyeVec = posWorld.xyz - _WorldSpaceCameraPos;
float3 normalWorld = UnityObjectToWorldNormal(v.normal);
float4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);
float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w);
o.tangentToWorldAndPackedData[].xyz = tangentToWorld[];
o.tangentToWorldAndPackedData[].xyz = tangentToWorld[];
o.tangentToWorldAndPackedData[].xyz = tangentToWorld[];
//We need this for shadow receving
UNITY_TRANSFER_SHADOW(o, v.uv1);
o.ambientOrLightmapUV = VertexGIForward(v, posWorld, normalWorld);
UNITY_TRANSFER_FOG(o,o.pos);
return o;
} half4 BRDF (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi)
{
float3 halfDir = Unity_SafeNormalize (float3(light.dir) + viewDir); half nl = saturate(dot(normal, light.dir));
float nh = saturate(dot(normal, halfDir));
half nv = saturate(dot(normal, viewDir));
float lh = saturate(dot(light.dir, halfDir)); // Specular term
half perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness);
half roughness = PerceptualRoughnessToRoughness(perceptualRoughness); // GGX Distribution multiplied by combined approximation of Visibility and Fresnel
// See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course
// https://community.arm.com/events/1155
half a = roughness;
float a2 = a*a; float d = nh * nh * (a2 - .f) + 1.00001f;
float specularTerm = a2 / (max(0.1f, lh*lh) * (roughness + 0.5f) * (d * d) * ); // on mobiles (where half actually means something) denominator have risk of overflow
// clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)
// sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))
#if defined (SHADER_API_MOBILE)
specularTerm = specularTerm - 1e-4f;
#endif #if defined (SHADER_API_MOBILE)
specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
#endif
half surfaceReduction = (0.6-0.08*perceptualRoughness);
surfaceReduction = 1.0 - roughness*perceptualRoughness*surfaceReduction; half grazingTerm = saturate(smoothness + (-oneMinusReflectivity));
half3 color = (diffColor + specularTerm * specColor) * light.color * nl
+ gi.diffuse * diffColor
+ surfaceReduction * gi.specular * FresnelLerpFast (specColor, grazingTerm, nv); return half4(color, );
} half4 frag ( v2f i):SV_Target
{
FRAGMENT_SETUP(s)
UnityLight mainLight = MainLight ();
UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld); half occlusion = Occlusion(i.tex.xy);
UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, mainLight);
half4 c = BRDF (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect);
c.rgb += Emission(i.tex.xy); UNITY_APPLY_FOG(i.fogCoord, c.rgb);
c.a = 1.0;
return c;
} ENDCG
}
}
CustomEditor "StandardShaderGUI"
}
效果图
效果完全一致,继续删减keyword
PBR工作流的更多相关文章
- LearnOpenGL.PBR.工作流贴图
- PBR Metallic/Roughness工作流中Albedo与F0的计算方法
首先简单回顾一下典型的纯金属与绝缘体的PBR属性: 纯金属: Albedo(diff): 0 F0(spec): >0.3 (or 0.5, epic/allegorithmic etc.) M ...
- 【翻译】CEDEC2014 CAPCOM 照相机正确的照片真实的制作工作流
这次带来的翻译是Capcom在CEDEC2014上发表的技术美术相关的资料.资料的目的,就是在已经拥有了一套基于物理的渲染引擎的前提下,如何进行图片真实的材料的拍摄并制作为引擎里的材质,以及如何正确 ...
- 关于unity里pbr技术和材质 unity5默认shader和传统的对比
刚开始也不知道什么是pbr (Physically Based Rendering)后来才发现这是一种新的渲染方式 与之对应的是材质是pbs(Physically Based Shader) unit ...
- 由浅入深学习PBR的原理和实现
目录 一. 前言 1.1 本文动机 1.2 PBR知识体系 1.3 本文内容及特点 二. 初阶:PBR基本认知和应用 2.1 PBR的基本介绍 2.1.1 PBR概念 2.1.2 与物理渲染的差别 2 ...
- LearnOpenGL.PBR.理论
判断一种PBR光照模型是否是基于物理的,必须满足以下三个条件: ()基于微平面(Microfacet)的表面模型.Be based on the microfacet surface model. ( ...
- Oozie分布式任务的工作流——Spark篇
Spark是现在应用最广泛的分布式计算框架,oozie支持在它的调度中执行spark.在我的日常工作中,一部分工作就是基于oozie维护好每天的spark离线任务,合理的设计工作流并分配适合的参数对于 ...
- Oozie分布式任务的工作流——邮件篇
在大数据的当下,各种spark和hadoop的框架层出不穷.各种高端的计算框架,分布式任务如乱花般迷眼.你是否有这种困惑!--有了许多的分布式任务,但是每天需要固定时间跑任务,自己写个调度,既不稳定, ...
- 解析大型.NET ERP系统核心组件 查询设计器 报表设计器 窗体设计器 工作流设计器 任务计划设计器
企业管理软件包含一些公共的组件,这些基础的组件在每个新项目立项阶段就必须考虑.核心的稳定不变功能,方便系统开发与维护,也为系统二次开发提供了诸多便利.比如通用权限管理系统,通用附件管理,通用查询等组件 ...
随机推荐
- iOS开发25个性能调优技巧
1. 用ARC管理内存 ARC(Automatic Reference Counting, 自动引用计数)和iOS5一起发布,它避免了最常见的也就是经常是由于我们忘记释放内存所造成的内存泄露.它自动为 ...
- 5.14日学习内容1:jquery表单相关知识
<script> $comment.animate({height:'+=50'},400);//在原来的基础上加50: $('.smaller').click(function(){ i ...
- vim 设置
TL;DR: $ git clone https://github.com/sontek/dotfiles.git $ cd dotfiles $ ./install.sh vim Download ...
- hdu1569 方格取数(2) 最大点权独立集=总权和-最小点权覆盖集 (最小点权覆盖集=最小割=最大流)
/** 转自:http://blog.csdn.net/u011498819/article/details/20772147 题目:hdu1569 方格取数(2) 链接:https://vjudge ...
- MapReduce实战(一)自定义类型
需求: 处理以下流量数据,第1列是手机号,第7列是上行流量,第8列是下行流量.将手机号一样的用户进行合并,上行流量汇总,下行流量也汇总,并相加求得总流量. 1363157985066 13726230 ...
- gitlab手残点错关闭注册No authentication methods configured on login page
Gitlab - 如何解決 "No authentication methods configured on login page" ? (gitlab version : 8.1 ...
- Java同步锁全息详解
一 同步代码块 1.为了解决并发操作可能造成的异常,java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块.其语法如下: synchronized(obj){ // ...
- JQ实现吸顶效果代码
吸顶下过代码跟简单几行代码就可以了 如果滚动的军力大于100,就改变导航的定位方式,否则就默认 $(function(){ $(window).scroll(function(){ ...
- jfinal_BLOG v1.0
http://git.oschina.net/fleam/jfinal_AmazeUI
- 第一百三十八节,JavaScript,封装库--插件
JavaScript,封装库--插件 库主要是用来封装一般JavaScript的常规操作代码,而拖拽这种特效代码属于功能性代码,并不是必须的,所以这种类型的代码,我们建议另外封装,在需要的时候作为插件 ...