三言两语说shader(九)钻石

这次的目标是绘制一颗闪闪发光的钻石,追求效果是越接近真实越好。

先说说为此我这几天干了些什么。

1.看了stalendp blog里那篇《钻石效果》后头的参考文献

最有价值的就是ATI在2004年GDC上作的演讲,题目就叫Drawing a Diamond。但是由于只有ppt,所以很难重现工程深入学习。思路大概是预备了一张折射CubeMap,一张带模拟色散的反射CubeMap,最后再加上耀斑混合而成。

2.试验了一些简单的镜面反射

非实时的CubeMap反射最简单没什么好说的,需要注意的是要做到位置精确的镜面反射还是很难的,一不小心就会发现当物体离反射面很近时,尺寸位置都会严重错误。

像下图这样,球的镜像明显尺寸过大了。

实时获取环境CubeMap的反射也测试了下,没记错的话上图CubeMap的FaceSize设的是512,在我的一加手机上基本就跑不到正常帧了,FaceSize设成256帧率还可以接受,但画质就没法看了,总之实时反射是手机无法承受的。

这里我总结一点思想:

游戏画面表现本身就带有很多虚假的成分,因为全按真实的来计算性能根本无法承受,最简单的拿光照来说,不谈多次反射的环境光,单纯的一次平行光照实时计算都没法满足,都要预烘焙光照贴图,阴影也一样,真实的计算太费性能,一般也弄个假的完事。

所以我们会发现游戏画面和现实照片的差距总是一目了然。所以会出现很多专门掩饰的技巧。

如何抓住主要部分,在效果和性能间做出取舍,而又能尽量达到接近真实的效果,大概就是图形工程师们大多数时候在思考的事情了。

这个钻石效果的演示demo,明显要按固定环境的思路来,不需要考虑实时问题,像前面提到的那种尺寸扭曲的问题,基本也是可以忽略的,因为钻石形状类似小球,环境的轻微扭曲是能被轻易掩饰过去的。

3.到AssetStore搜索了下相关资源

发现免费的Gem Shaders就是Unity官方提供的,而且还为5.0专门更新过一次。我跑了下它的示例场景,感觉观感不是很满意。

主要是它提供的几个钻石模型形状我也不喜欢,于是自己到网上找了个大概是传说中“八星八箭”形状的模型,然后还是用这个shader试了下效果,结果发现还是很屌的。

我原本的想法是尽量用真实的光线折射、反射计算得到最终效果的。但是就算暂不考虑代码实现,不考虑实现后的性能如何,首先单纯就目前凭我有限的光学姿势能不能建立起真实准确的光学模型都很明显是成问题的。

所以这次我们还是以学习官方的示例为主,不要整天想自己搞什么大新闻了。

代码如下:

Shader "FX/Gem"
{
	Properties {
		_Color ("Color", Color) = (1,1,1,1)
		_ReflectionStrength ("Reflection Strength", Range(0.0,2.0)) = 1.0
		_EnvironmentLight ("Environment Light", Range(0.0,2.0)) = 1.0
		_Emission ("Emission", Range(0.0,2.0)) = 0.0
		[NoScaleOffset] _RefractTex ("Refraction Texture", Cube) = "" {}
	}
	SubShader {
		Tags {
			"Queue" = "Transparent"
		}
		// First pass - here we render the backfaces of the diamonds. Since those diamonds are more-or-less
		// convex objects, this is effectively rendering the inside of them.
		Pass {

			Cull Front
			ZWrite Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			struct v2f {
				float4 pos : SV_POSITION;
				float3 uv : TEXCOORD0;
			};

			v2f vert (float4 v : POSITION, float3 n : NORMAL)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v);

				// TexGen CubeReflect:
				// reflect view direction along the normal, in view space.
				float3 viewDir = normalize(ObjSpaceViewDir(v));
				o.uv = -reflect(viewDir, n);
				o.uv = mul(_Object2World, float4(o.uv,0));
				return o;
			}

			fixed4 _Color;
			samplerCUBE _RefractTex;
			half _EnvironmentLight;
			half _Emission;
			half4 frag (v2f i) : SV_Target
			{
				half3 refraction = texCUBE(_RefractTex, i.uv).rgb * _Color.rgb;
				half4 reflection = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.uv);
				reflection.rgb = DecodeHDR (reflection, unity_SpecCube0_HDR);
				half3 multiplier = reflection.rgb * _EnvironmentLight + _Emission;
				return half4(refraction.rgb * multiplier.rgb, 1.0f);
			}
			ENDCG
		}

		// Second pass - here we render the front faces of the diamonds.
		Pass {
			ZWrite On
			Blend One One

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			struct v2f {
				float4 pos : SV_POSITION;
				float3 uv : TEXCOORD0;
				half fresnel : TEXCOORD1;
			};

			v2f vert (float4 v : POSITION, float3 n : NORMAL)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v);

				// TexGen CubeReflect:
				// reflect view direction along the normal, in view space.
				float3 viewDir = normalize(ObjSpaceViewDir(v));
				o.uv = -reflect(viewDir, n);
				o.uv = mul(_Object2World, float4(o.uv,0));
				o.fresnel = 1.0 - saturate(dot(n,viewDir));
				return o;
			}

			fixed4 _Color;
			samplerCUBE _RefractTex;
			half _ReflectionStrength;
			half _EnvironmentLight;
			half _Emission;
			half4 frag (v2f i) : SV_Target
			{
				half3 refraction = texCUBE(_RefractTex, i.uv).rgb * _Color.rgb;
				half4 reflection = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.uv);
				reflection.rgb = DecodeHDR (reflection, unity_SpecCube0_HDR);
				half3 reflection2 = reflection * _ReflectionStrength * i.fresnel;
				half3 multiplier = reflection.rgb * _EnvironmentLight + _Emission;
				return fixed4(reflection2 + refraction.rgb * multiplier, 1.0f);
			}
			ENDCG
		}

		// Shadow casting & depth texture support -- so that gems can
        // cast shadows
        UsePass "VertexLit/SHADOWCASTER"
	}
}

下面我来分析其中的姿势点:

个人水平有限很多说法可能是错的,这里申明下先。

1.折射光的模拟

首先它的折射光并没有真实计算,而是给了这样一张CubeMap图,然后实际上是通过反射在计算。

2.pass设计

大致分两步,第一个pass是绘制钻石的背面,第二个当然就是前面了。注意第二个pass里的Blend One One,表示两个图层1:1混合。

考虑到钻石是个透明的物体,这样设计也是合情合理的。

3.光照模型设计

大致也是折射光、反射光、环境光、自发光各种叠加各种乘啦。反正因为白色是255,黑色是0,这里虽说算的是光,但实际处理的还是颜色,不管是加还是乘,都是越叠越亮,符合光线叠加的基本规律。至于它的计算公式是源自准确的光学原理,还是近似的模拟,我当然也不知道啦。

4.HDR

High Dynamic Range,不负责任的说下,大概就是假如0是黑色,1是白色,那么有些部分的颜色可以超过1,这样最终画面结果会呈现出一种亮处高闪耀的效果,具体请参考Unity官方文档说明。

5.菲涅尔效果

简单说就是因为光线射向玻璃时,一部分被反射,一部分直接射进去了,导致最终反射光线呈现一个视角越平行于入射面,反射效果越强,越垂直越弱的现象。

第二个pass计算时引入了这个因子。

所以一开始我说自己难以建立准确的模型,比如像这种细微而又真实存在的现象,仅凭一点粗浅的折射反射姿势而不去深入学习又怎么能考虑到呢?

这个shader里用了好多宏,在CGIncludes文件夹里的那些文件里都可以查到,这些我大致可以理解或者叫猜到怎么回事,但要真正弄清楚肯定得费一番功夫。

这里请允许猪哥偷个懒,随便说说装作懂了的样子,具体的计算细节就不一一展开了。

结语:

至此初阶段的shader学习计划就算完成了,由于我刻意赶了下进度,偷了些懒,比计划提前了一周。一分耕耘一分收获,shader这块水很深,想要有所造诣肯定得花更多的时间。但我出于全盘考虑目前并不想在这块继续投放精力。

下一阶段目光回到C#语言这块最基础的领域,继续修内力。去年买的《C#本质论》要收尾,一直没翻的《深入理解C#》也要看完。

谈语言难免会牵涉到设计模式,但也尽量避开。

周期定在五周吧。

时间: 2024-10-11 17:18:30

三言两语说shader(九)钻石的相关文章

Unity3D游戏开发从零单排(九) - 进击的Shader

提要 今天要学习的是一些Shader 的例子,从简单到难.Let's go. 一大波例子来袭 还是用上一篇用到的工程.点我下载 红色的螃蟹 Test1.shader Shader "Custom/Test1" { SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float4 color : COLO

cocos2d-x学习笔记(九)使用shader创建鱼的投影

一. 1.先来看下效果图 貌似效果还可以 2.cocos2d-x的主要程序代码 Size size = Director::getInstance()->getWinSize(); auto sprite = Sprite::create("fish.png");   sprite->setPosition(size.width/2, size.height /2 );     auto shader_program = GLProgram::createWithFilen

【浅墨Unity3D Shader编程】之九 深入理解Unity5中的Standard Shader (一)&屏幕水幕特效的实现

本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/49556461 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 本文工程使用的Unity3D版本: 5.2.1 概要:本文主要介绍了Unity5中的标准着色器,并且也涉及到了基于物理的着色.延迟渲染等高级着色技术,而在文章后半部分,也对屏幕水幕特效的实现方法进行了讲解与分析. 依然是附上

【OpenGL】Shader实例分析(九)- AngryBots中的主角受伤特效

转发请保持地址:http://blog.csdn.net/stalendp/article/details/40859441 AngryBots是Unity官方的一个非常棒的样例.非常有研究价值. 曾经研究的时候.因为其内容丰富,一时间不知道从哪入手写文章分析. 这一段时间研究shader技术比較多一些,就从shader的这一方面開始吧.首先分析当中的一个屏幕特效:当主角受到攻击时会出现的全屏效果(postScreenEffect).效果例如以下: 事实上这是一种的Bloom效果,相关文件有:M

火云开发课堂 - 《Shader从入门到精通》系列 第十九节:在Shader中实现3D模型的UV动画

<Shader从入门到精通>系列在线课程 优惠链接:http://edu.csdn.net/combo/detail/90 第十一节:在Shader中实现3D模型的UV动画 视频地址: http://edu.csdn.net/course/detail/1441/22683?auto_start=1 交流论坛:http://www.firestonegames.com/bbs/forum.php 工程下载地址:请成为正式学员获取工程 课程截图: 版权声明:本文为博主原创文章,未经博主允许不得转

引擎设计跟踪(九.14.2d) [翻译] shader的跨平台方案之2014

Origin: http://aras-p.info/blog/2014/03/28/cross-platform-shaders-in-2014/ 简译 translation: 作者在2012年写过一篇shader跨平台的文章, 开始提到了并有链接. 1.手写或者宏替换 使用宏定义将 HLSL & GLSL 的不同之处封装, 并让每个开发人员了解他们的不同之处. 例子: Valve的Source 2引擎 优点: 简单,容易实现缺点: 每个开发者都必须熟悉使用宏定义库, 还有其他语法上的不同.

Cocos2d-x项目移植到WP8系列之九:使用自定义shader

有时候想得到一些例如灰度图等特殊的渲染效果,就得用到自定义shader,关于shader的一些背景知识,自行谷歌,列出两篇cocos2dx里介绍shader的相关文章 http://blog.csdn.net/while0/article/details/9666829     http://blog.sina.com.cn/s/blog_aa01f7030101mdom.html cocos2dx在wp8平台不知道是不是在渲染时OpenGL要转成D3D的原因还是其他原因,不能在运行时编译链接s

Unity3D Shader官方教程翻译(十九)----Shader语法,编写表面着色器

Writing Surface Shaders Writing shaders that interact with lighting is complex. There are different light types, different shadow options, different rendering paths (forward and deferred rendering), and the shader should somehow handle all that compl

【Unity Shader】(九) ------ 高级纹理之渲染纹理及镜子与玻璃效果的实现

笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Shader](三) ------ 光照模型原理及漫反射和高光反射的实现 [Unity Shader](四) ------ 纹理之法线纹理.单张纹理及遮罩纹理的实现 [Unity Shader](五) ------ 透明效果之半透明效果的实现及原理 [Unity Shader](六) ------ 复杂的光照(上) [Unity