三言两语说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#》也要看完。

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

周期定在五周吧。

时间: 04-15

三言两语说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 工程下载地址:请成为正式学员获取工程 课程截图: 版权声明:本文为博主原创文章,未经博主允许不得转

我不是九爷 带了解 Unity3D与VR虚拟现实

对于大多数人来说,可能不知道Unity3D是什么,但是却知道VR虚拟现实是什么,更不会把VR虚拟现实和Unity3D联系在一起,外行的人根本不知道这两者之间有什么关系.那么,今天来给你讲解一下Unity3D与VR虚拟现实之间的区别和联系分别是什么? 什么是Unity3D?Unity3D是一软专业3D游戏引攀,其具备跨平台发布.离效能优化.高性价比,AAA级游戏画面演染效果等特点.目前Unity3D应用范围广泛,从手机游戏到联网的大型游戏,从严肃游戏到电子商务,再到VR虚拟现实均可完美呈现. 什么

【浅墨Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&amp;颜色、光照与材质

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40955607 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 邮箱: [email protected] 本篇文章中,我们学习了Unity Shader的基本写法框架,以及学习了Shader中Properties(属性)的详细写法,光照.材质与颜色的具体写法.写了6个Shader作为本文S

【Shader拓展】Illustrative Rendering in Team Fortress 2

写在前面 早在使用ramp texture控制diffuse光照一文就提到了这篇著名的论文.Valve公司发表的其它成果可见这里.这是Valve在2007年发表的一篇非常具有影响力的文章,我的导师也提到过这篇,既然这么有名.我就去拜读了下,结果真是读到头大啊啊啊啊!事实上半年前就读了这篇文章.差一点读完.后来一比赛一考试一放假就没完毕,对自己非常愧疚. ..时隔这么久希望能把之前的理解整理出来,顺便看看能不能有很多其它收获.(又要读一遍E文!) 这篇博客在翻译这篇论文的基础上.会在Unity S

Unity3D教程宝典之Shader篇

教程目录 基础讲:Shader学习方法基础讲:基础知识特别讲:常见问题解答特别讲:CG函数 第一讲: Shader总篇第二讲: Fixed Function Shader 第三讲: Vertex&Fragment Shader基础 第四讲: 制作一个美丽的地球第五讲:LOGO闪光效果 第六讲:TexGen第七讲:流程图第八讲:Why CG?第九讲:Render Path第十讲:雾化第十一讲:阴影面剔除及深度测试第十二讲:alpha测试第十三讲:alpha混合第十四讲:Surface Shader