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系统核心组件 查询设计器 报表设计器 窗体设计器 工作流设计器 任务计划设计器
企业管理软件包含一些公共的组件,这些基础的组件在每个新项目立项阶段就必须考虑.核心的稳定不变功能,方便系统开发与维护,也为系统二次开发提供了诸多便利.比如通用权限管理系统,通用附件管理,通用查询等组件 ...
随机推荐
- DDR 复位
将FPGA代码和实际的数字电路对应起来. always @ (negedge clk_ref_200) begin if(ddr3_init_done) 'b10) 'b10 ...
- Sublime Text 2中自定义代码模板
Sublime Text 2中自定义代码模板 2012-12-06 10:13 9921人阅读 评论(0) 收藏 举报 分类: 编辑器-Sublime Text 2(5) 版权声明:本文为博主原创 ...
- NiftyNet 项目了解
1. NiftyNet项目概述 NiftyNet项目对tensorflow进行了比较好的封装,实现了一整套的DeepLearning流程.将数据加载.模型加载,网络结构定义等进行了很好的分离,抽象封装 ...
- QT .pro文件 LIBS用法详解
在程序中需要使用到团队其它成员开发的静态库和动态库,起初是知道使用LIBS变量在在.pro文件中指定需要包含的库,但是实际使用的时候却遇到很大麻烦,但其实确实是因为自己看官方文档不太用心造成的. 下面 ...
- 如何输出long double?
首先long double是C99引入的,但是如何printf格式化一个long double的数据的呢? scanf一个double数据,是%lf,printf一个float或者double都是%f ...
- 【问题记录】javaweb项目的jar无法识别注解的bean
问题:eclipse中javaweb项目,打成jar包供其它项目使用,发现无法识别使用注解的bean. 原因参考: http://blog.csdn.net/wangpeng047/article/d ...
- hadoop集群的配置文件
export JAVA_HOME=/home/hadoop/apps/jdk1..0_131 1.hadoop-env.sh中配置JAVA_HOME export HADOOP_YARN_USER=/ ...
- nginx配置事例
#user nobody; worker_processes 4; #error_log logs/error.log; #error_log logs/error.log notice; #erro ...
- javascript存储器属性与数据属性
在新的js规范中,我们又多了几种定义属性的方法.给一个对象添加属性,以前可能是这样的 var o = {name: '未起名';} 现在可以这样子 var o = {get name(){return ...
- spring 的redis操作类RedisTemplate
spring 集成的redis操作几乎都在RedisTemplate内了. 已spring boot为例, 再properties属性文件内配置好 redis的参数 spring.redis.host ...