本系列文章由@浅墨_毛星云 出品。转载请注明出处。

文章链接:http://blog.csdn.net/poem_qianmo/article/details/40723789

作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442

邮箱: happylifemxy@163.com

作为一个系统介绍Unity3D中Shader编写的系列文章的开篇。本文的第一部分为系列文章的前言,然后第二部分介绍了这个系列文章中我们会使用的游戏场景创建方式。最后一部分解说了怎样在Unity中创建和使用Shader。为后面专注于介绍怎样在Unity中进行Shader编程打好了基础。

由于后面推出的系列文章会着重介绍各种Shader的写法和实现,不会再详细解说怎样创建场景和写出Shader代码后怎样使用。相信这篇文章作为本系列的开篇。发表出来肯定还是会对大家多少有些帮助的。

大家以后阅读稍后推出的Unity Shader系列文章的时候。有场景创建或者Shader代码写好了怎样使用方面的疑问的话,能够随时回过头来查阅这篇文章。

OK,就让我们从这篇文章開始一趟精彩万分的Shader游戏编程旅途。

依然国际惯例。看几张文章中实现的场景美图先:

PS:文章配套的三个unitypackage和终于的project源代码在文章末尾处提供下载。

一、系列文章前言

在这个系列开头。浅墨想说的是,事实上这个系列文章中我们学的主要是着色器编程技术,重点不是学Unity。

甚至能够这样说。我们学的是HLSL——没错,就是DirectX中的那个HLSL。

为什么这样讲,让我们来捋一捋。

首先,Unity中编写Shader的语言叫做ShaderLab,而ShaderLab说白了就是裹着一层皮的CG着色器语言而已。

Cg,即C forgraphics。即用于图形的C语言,是微软Microsoft和英伟达NVIDIA相互协作在标准硬件光照语言的语法和语义上达成的一种一致性协议。

HLSL和CG事实上是同一种语言(參见Cg教程_可编程实时图形权威指南29页的致谢部分)。非常多时候,我们会发现用HLSL写的代码能够直接其中Cg代码使用。

Microsoft和NVIDIA联手推出CG语言。想在经济和技术上实现双赢。从而通过这样的方式联手打击他们共同的对手GLSL。

既然Unity主打Shader编程的语言ShaderLab是CG语言披上一层皮。而CG语言又约等于HLSL。这就是说,在Unity中写Shader约等于用HLSL写Shader。也就约等于给DirectX写Shader。

尽管有点绕orz,最后总结一下也就是:

在Unity中写Shader约等于给DirectX写Shader

而Unity又是这样一个集万千宠爱于一身的可见即所得的眼下在移动互联网时代火到不行的游戏引擎。

能够说。Unity可见即所得的开发环境非常适合Shader的学习。并且在Unity分分钟能够创建出来一个美丽的场景里面写写Shader,心情都会好非常多。不再是苦逼地面朝代码背朝天了。

所以浅墨决定開始在Unity中进行这个shader学习系列。毕竟Unity、CG、HLSL不分家。

另外促成这个系列文章的成型的一个原因是Unity的Asset store。浅墨非常喜欢Unity引领的Asset store这样一站式的游戏素材商店,里面有数不尽的游戏插件、素材、资源、脚本代码參考、shader资料等等,甚至现成的游戏project范例。

Asset store给开发人员们带来了非常大的便利,简直就是游戏开发界的大宝库。

但须要说明的是,Unity这款引擎的缺点也是非常明显的。比方Unity不正确外开源。我们看不到源代码,坑比較多。遇到坑了看不到

源代码给解决带来了非常大难度。

运行效率还是不太行,渲染大场景和做高级渲染的时候还是显得力不从心。比方渲染大片的近乎

真实的动态水面时,帧数就立刻降下来了。须要后期做非常多的优化。就画面质量来说和Unreal Engine和cryEngine等次时代

引擎比还是有差距。是近期几年互联网的浪潮和得屌丝者的天下商业模式促进了其近期几年如此的成功。

说了这么多,总结一下。

Unity仅仅是我们学习CG、HLSL编程可见即所得的好帮手好工具而已。

我们主要还是利用它来更好的学计算机图形学和Shader编程,顺便掌握眼下热门的Unity引擎的基本使用和研发思路。

我们还是忘不了C++和DirectX,我们还是渴望通过自己的努力,终于有能力用C/C++一句一句写出自己的游戏引擎,我们还是想从零開始造轮子。毕竟那样一句一句写出来的代码都是自己的,而不是那些别人为我们准备好的现成的API函数。

谨以此前言,与诸君共勉。

二、用Unity创建第一个漂亮的游戏场景

首先要说明的是,作为一个从入门内容開始逐渐深入介绍的系列教程,这一部分在Unity下创建场景的内容是为还不太熟悉Unity的朋友们准备的,假设你已经熟悉了Unity的使用,这部分能够高速跳读。

OK,正式開始吧。

2.1 【第一步】当然是新建一个项目

大家肯定都知道,每次新建项目后或者新建场景后,会得到一片Unity中默觉得全蓝的场景。想要场景变得有生机。一般都是去菜单条的GameObject里面新建Terrain(地形)然后进行编辑。

Terrain的制作非常耗时并且水非常深,想要精通也是得花一些功夫。

甚至Unity Asset Store中有各种能够辅助高速生成AAA级大作风格的真实场景的插件,如Terrain Composer,配合着Unity中有名的Relief Terrain Pack v3地形辅助着色渲染工具,能够生成近乎以假乱真的三维自然风光出来。

美丽地形的创建当然不属于我们解说的重点,网络上有数不清的文章和视频讲这方面的内容。有须要的话,大家去学一些基础,或者直接用Asset Store中现成的各种美丽场景。反正浅墨是各种在网上下载AssetStore中美工大牛们做场景。然后简单的改动,为自己測试和寻常调数值所用。

这不,浅墨为了写这篇博客,就为大家再加工“创作“了一个夏威夷风格的场景来:)

2.2 【第二步】导入Hawaii
Environment.unitypackage场景包

上文讲到,为了节约时间,浅墨提前为大家改动好了一个场景,然后这个场景已经打包。叫做“HawaiiEnvironment.unitypackage “。在文章末尾提供下载。(限于Unity对中文的支持拙计,无奈仅仅能取英文名。不然直接导入就报错)。

 
   HawaiiEnvironment.unitypackage单独下载请点我】

双击这个包,导入到我们空空如也的project中,经过一段时间的读条。就导入完成了。然后我们双击打开出如今Project面板中Assets目录下的场景文件Hawaii Environment.unity

接着便打开了场景,假设打开成功。Scene面板中应该便出现了例如以下类似的场景画面:

由于略去了场景编辑部分。直接导入,所以过程是很easy的。可是。这还全然不够。让我们在场景中加入一个能够自由控制的摄像机吧。

2.3 【第三步】加入第一人称摄像机

浅墨准备的这个场景包是没有摄像机的,单单就是场景。所以我们还须要在场景中加入一个摄像机。

大家应该清楚,比較常见加入摄像机的做法是通过菜单条中的GameObject->CreateOther->Camera来加入。

但这样的方式的摄像机是固定的,不合我们的要求。我们想加入的是一个在游戏执行时能够自由移动视角的第一人称摄像机。事实上Unity自带的资源包中刚好能够满足我们的要求。于是我们在Project面板中右键【Import Package】。或者菜单条中Assets->ImportPackage->Character
Controller来导入官网为我们准备的的角色控制资源包。例如以下图:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

点击之后,会弹出例如以下的资源导入确认窗体。我们直接点确定即可了:

由于这个包非常小。所以非常快就导入完毕。这时Assets根目录下会出现一个名为Standard Assets的目录。展开它或者点进去,就是名为【CharacterControllers】的目录,继续点进去。发现了一个胶囊状的叫【First PersonController】的家伙。这就是我们须要的了。

然后我们先在Scene面板中利用【右键+键盘W、A、S、D】以及滚轮等操作调整好场景,然后在我们刚才的【CharacterControllers】下点击这个胶囊装的【First Person Controller】按住不放。拖动到Scene场景中,选到合适的地方(如草坪上)后就放手,操作例如以下:

Unity会自己主动将这个【CharacterControllers】的中心位置依附到地形表面。

这个时候我们会发现之前是黑屏的Game面板中也有了画面:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

这时我们还要将这个 First Person Controller的底部向上拖动一点,不然执行游戏时我们会不停的往下掉,由于在Unity默认情况下First Person Controller的中心位于中部,这会照成它的底部已经穿透地形,悬空位于地形的下方,自然一执行就往下掉。

我们Hierarchy面板中选中First Person Controller,工具栏中选择【移动】工具

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

然后对着场景中胶囊上的代表Y轴的绿色箭头向上适当拖动,让胶囊的底部确保位于草地之上即可了。

这时候我们点击unity中间三角尖的【执行】button。就能够自由地在场景中观察和移动了~

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

Unity第一人称控制器默认操作方式类似CS。CF一类的FPS游戏。

W、A、S、D前后左右移动,空格跳跃,鼠标移动调整视角,很有亲切感有木有~

这就非常好地体现了Unity的入门easy的特点,仅仅用点点鼠标一个美丽的场景就展如今眼前。

2.4 【第四步】在游戏场景中增加背景音乐

话说这么漂亮的场景怎么能没有音乐?

最好还是就让我们加入一段优美的钢琴曲吧。曲子浅墨都为大家准备好了,上文已经导入的HawaiiEnvironment.unitypackage包中,在Assets根文件夹下包括了一首林海的《日光告别》。

我们要做的仅仅要是把这个音乐文件拖拽到第一人称摄像机First PersonController上就能够了,就像箭头中指的这样:

拖拽完毕后,First Person Controller的Inspector面板中就应该会多了一个Audio Source组件。

执行场景,伴随着漂亮的场景,“吹着海风”,优美的钢琴曲入耳,很怡人。

先放两张測试过程中的场景美图。再继续我们下一部分的解说吧:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">


很逼真的水效。採用大名鼎鼎的NGUI工作室Tasharen Entertainment出品的水面插件:

三、导入QianMo’s Toolkit并使用

3.1 认识QianMo's
Toolkit

所谓的QianMo's Toolkit,事实上就是浅墨为场景測试写的一个小脚本工具集,打包成一个unitypackage方便多次使用而已。若有须要。浅墨会在当中加入很多其它的功能。

以后我们每次新建project的时候。仅仅要导入这个小工具就能够使用我们之前已经写好的各种特性,很便捷。

QianMo's Toolkit v1.0.unitypackage单独下载请点我】

QianMo's Toolkit v1.0版的内容例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

也就是包括了五个脚本文件,两张图片。这五个脚本文件的功能分别为:

ShowFPS:在游戏执行时显示帧率相关信息

ShowObjectInfo:在场景中和游戏窗体中分别显示加入给随意物体文字标签信息。隐藏和显示可选,基于公告板技术实现。

ShowGameInfo:在游戏执行时显示GUI相关说明

ShowLogo:在游戏执行时显示Logo

ShowUI:在游戏执行时显示简单的镶边UI。

这个五个脚本的代码浅墨都已经具体凝视。在兴许文章中有机会我们会介绍其具体写法。这篇文章中就先简单的认识一下他们就好。

PS:下文第四节中贴出了ShowGameInfo脚本的所有代码。

3.2 使用QianMo's
Toolkit

上文已经说了,既然这是一个unitypackage,那么仅仅用双击它导入到我们当前的项目中即可了。

导入完毕之后。Assets目录下就又多了一个名为” QianMo's Toolkit v1.0“的目录,内容就是我们刚才介绍的5个脚本文件两张图:

临时我们要使用的是ShowGameInfo、ShowLogo、ShowUI这三个脚本文件,把它们一起拖到我们之前创建的第一人称摄像机First Person Controller上即可了:

拖动完毕后。我们在First Person Controller的Inspector面板中发现其多了三个组件。就是我们给他加入的这个三个脚本:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

事实上Show Logo无关紧要,就是显示了浅墨自己的logo而已,当然你能够换成自己的logo。show UI也无关紧要,就是显示了一个顶部的镶边png。基本的是这个ShowGameInfo,它是用于显示帧率等相关文字消息的。

(事实上简约党会认为三个都无关紧要,orz)

拖动完毕之后,再次执行,我们来看一看效果:

能够发现。游戏窗体的边边角角多了一些说明和图片。以及有了帧率的显示。

四、书写和使用第一个Shader

上文解说的都是一般的场景构建过程,接下来就要正式開始我们的核心部分。书写第一个Shader了。

在完毕上文中解说的创建好美丽的场景之后。我们首先能够在Assets根文件夹下创建一个文件夹。用于以后Shader和Material文件的存放。创建过程能够是在Project面板的空白处右键->Create->Folder。也能够是点击Project面板中的Create下拉菜单->Folder

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

给新出来的这个目录取名Shaders,然后回车。然后再用相同的方法在Assets根目录下创建一个名为Textures的目录,用于稍后素材图片的存放。那么,假设你依照浅墨依照眼下描写叙述的步骤来的话。Assets根目录到如今就是这种5个目录:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

接着。进去到我们创建的Shader目录。

相同在空白处右键->Create->Shader,或者是直接点Create下拉菜单->Shader,创建一个Shader文件。取名为   “0.TheFirstShader”。

然后双击打开它,Unity会默认使用名为MonoDevelop的编辑器打开这个Shader文件。

小tips:能够在菜单条中Edit->Preferences->ExternalTools中调成默认用Visual Studio打开,但未经改动配置文件的Visual Studio对Shader后缀的文件是不支持语法高亮的。浅墨改动了部分配置文件才让Visual Studio支持了Unity Shader书写的语法高亮。对于不太清楚怎样改动的朋友,过些天浅墨会单独发一篇名为《Unity中使用Visual Studio编写shader并设置代码高亮》的文章来专门解说。

作为初次写Shader。我们暂且先用MonoDevelop顶一顶。后面的文章再换用改动了配置文件的Visual Studio。

好了,用MonoDevelop打开我们新建的这个Shader文件,发现Unity已经为我们写好了非常多代码。

我们最好还是自己又一次写点不一样的东西。

删掉原本的这些代码,拷贝浅墨写的例如以下代码到编辑器中:

  1. //-----------------------------------------------【Shader说明】----------------------------------------------
  2. //           Shader功能:   凹凸纹理显示+自选边缘颜色和强度
  3. //    使用语言:  Shaderlab
  4. //    开发所用IDE版本号:Unity4.5 06f 、Monodevelop
  5. //    2014年11月2日  Created by 浅墨
  6. //    很多其它内容或交流请訪问浅墨的博客:http://blog.csdn.net/poem_qianmo
  7. //---------------------------------------------------------------------------------------------------------------------
  8. Shader "浅墨Shader编程/0.TheFirstShader"
  9. {
  10. //-------------------------------【属性】-----------------------------------------
  11. Properties
  12. {
  13. _MainTex("【纹理】Texture", 2D) = "white" {}
  14. _BumpMap("【凹凸纹理】Bumpmap", 2D) = "bump" {}
  15. _RimColor("【边缘颜色】Rim Color", Color) =(0.17,0.36,0.81,0.0)
  16. _RimPower("【边缘颜色强度】Rim Power", Range(0.6,9.0)) = 1.0
  17. }
  18. //----------------------------【開始一个表面着色器】---------------------------
  19. SubShader
  20. {
  21. //渲染类型为Opaque。不透明
  22. Tags{ "RenderType" = "Opaque" }
  23. //-------------------開始CG着色器编程语言段-----------------
  24. CGPROGRAM
  25. //使用兰伯特光照模式
  26. #pragmasurface surf Lambert
  27. //输入结构
  28. structInput
  29. {
  30. float2uv_MainTex;//纹理贴图
  31. float2uv_BumpMap;//法线贴图
  32. float3viewDir;//观察方向
  33. };
  34. //变量声明
  35. sampler2D_MainTex;//主纹理
  36. sampler2D_BumpMap;//凹凸纹理
  37. float4_RimColor;//边缘颜色
  38. float_RimPower;//边缘颜色强度
  39. //表面着色函数的编写
  40. voidsurf (Input IN, inout SurfaceOutput o)
  41. {
  42. //表面反射颜色为纹理颜色
  43. o.Albedo= tex2D (_MainTex, IN.uv_MainTex).rgb;
  44. //表面法线为凹凸纹理的颜色
  45. o.Normal= UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
  46. //边缘颜色
  47. halfrim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
  48. //边缘颜色强度
  49. o.Emission= _RimColor.rgb * pow (rim, _RimPower);
  50. }
  51. //-------------------结束CG着色器编程语言段------------------
  52. ENDCG
  53. }
  54. //“备胎”为普通漫反射
  55. Fallback"Diffuse"
  56. }

因为这是第一篇Shader系列文章,已经涉及到非常多内容了,所以浅墨不可能展开解说这段代码的具体思路和写法。只是已经具体凝视,大家应该会多少有点明确。

随着稍后文章的深入,这段代码就显得非常easy易懂了。

拷贝完毕,保存一下这段代码,unity会自己主动检測和编译被保存的代码,我仅仅需返回Unity窗体,等待编译完毕就可以。若没有错误。在“0.TheFirstShader”的inspector面板中得到的结果应该是有红色的错误提示的。

须要注意的是。Shader想要使用到游戏物体上,一般得有个媒介。这个媒介就是我们的老朋友——材质(Material)。我们把Shader作用于材质,接着再把材质相应地作用于给游戏物体,这样写的Shader就间接地给物体表面使用了。

而这一层关系。在Unity中全然能够通过点点鼠标,拖动来完毕。以下我们就来讲一讲怎样将一个着色程序的结果显示到物体表面上。

知道以上原理了就非常easy了,在“0.TheFirstShader.shader”的同一文件夹下创建一个Material。相同是能够通过Create下拉菜单->Material或者空白处右键->create->Material来完毕。

为了到时候方便相应,我们将这个材质也取名为0.TheFirstShader。

接着,将0.TheFirstShader.shader拖动到0.TheFirstShader材质身上然后释放。

拖动完毕后,我们单击0.TheFirstShader材质,打开他的面板,发现他已经和一開始不一样了,泛着蓝光:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

还没完。接下来我们还得给这个材质加入两张纹理图片。图片浅墨也已经提前准备好了,在名为Textures01 by QianMo.unitypackage的Unity包中。相同双击这个包然后打开导入到项目中。

Textures01 by QianMo.unitypackage单独下载请点我】

我们在Textures目录下找到这两张纹理。接下来做的就是将他们拖动到0.TheFirstShader材质相应的纹理区域中,例如以下:

或者点击这里的Select分别选择,操作例如以下:

两张纹理选择完成后,我们的材质就准备好了。最后的结果,有点黑科技,如各种科幻游戏和电影中发光的矿石,很炫酷:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

OK,那么就仅仅剩下最后一步了,就是在场景中创建一个物体,然后将我们做好的材质拖拽到物体身上赋给这个物体即可了。

菜单条【GameObject】->【Create Other】->【Capsule】或者【Create】下拉菜单->【Capsule】来在场景中创建一个胶囊装的物体。

把他拖动到和我们的第一人称摄像机【First Person Controller】非常近的地方,这样方便观察,接着就能够把我们的“0.TheFirstShader”材质直接拖拽给场景中的这个胶囊,或者Hierachy面板中【Capsule】名字上即可了。操作例如以下图中的箭头所看到的:

经过拖拽,Capsule加上Material后的效果例如以下:

4.1 给使用Shader的物体加上文字说明

为了以后介绍多个Shader写法时能更清晰更方便,浅墨专门在QianMo’s Toolkit中做了一个能够在场景中和游戏窗体中分别显示附加给随意物体文字标签信息的工具脚本,叫做ShowObjectInfo。其具体凝视的代码例如以下:

  1. //-----------------------------------------------【脚本说明】-------------------------------------------------------
  2. //      脚本功能:    在场景中和游戏窗体中分别显示给随意物体附加的文字标签信息
  3. //      使用语言:   C#
  4. //      开发所用IDE版本号:Unity4.5 06f 、Visual Studio 2010
  5. //      2014年10月 Created by 浅墨
  6. //      很多其它内容或交流,请訪问浅墨的博客:http://blog.csdn.net/poem_qianmo
  7. //---------------------------------------------------------------------------------------------------------------------
  8. //-----------------------------------------------【用法】-------------------------------------------------------
  9. //      第一步:在Unity中拖拽此脚本到某物体之上,或在Inspector中[Add Component]->[浅墨's Toolkit v1.0]->[ShowObjectInfo]
  10. //      第二步:在Inspector里,Show Object Info 栏中的TargetCamera參数中选择需面向的摄像机,如MainCamera
  11. //      第三步:在text參数里填须要显示输出的文字。
  12. //      第四步:完毕。

    执行游戏或在场景编辑器Scene中查看显示效果。

  13. //      PS:默认情况下文本信息仅在游戏执行时显示。
  14. //      若须要在场景编辑时在Scene中显示,请勾选Show Object Info 栏中的[Show Info In Scene Editor]參数。

  15. //      同理,勾选[Show Info In Game Play]參数也能够控制是否在游戏执行时显示文本信息
  16. //---------------------------------------------------------------------------------------------------------------------
  17. //预编译指令。检測到UNITY_EDITOR的定义,则编译兴许代码
  18. #if UNITY_EDITOR
  19. //------------------------------------------【命名空间包括部分】----------------------------------------------------
  20. //  说明:命名空间包括
  21. //----------------------------------------------------------------------------------------------------------------------
  22. using UnityEngine;
  23. using UnityEditor;
  24. using System.Collections;
  25. //加入组件菜单
  26. [AddComponentMenu("浅墨's Toolkit v1.0/ShowObjectInfo")]
  27. //開始ShowObjectInfo类
  28. public class ShowObjectInfo : MonoBehaviour
  29. {
  30. //------------------------------------------【变量声明部分】----------------------------------------------------
  31. //  说明:变量声明部分
  32. //------------------------------------------------------------------------------------------------------------------
  33. public string text="键入你自己的内容 by浅墨";//文本内容
  34. public Camera TargetCamera;//面对的摄像机
  35. public bool ShowInfoInGamePlay = true;//是否在游戏执行时显示此信息框的标识符
  36. public bool ShowInfoInSceneEditor = false;//是否在场景编辑时显示此信息框的标识符
  37. private static GUIStyle style;//GUI风格
  38. //------------------------------------------【GUI 风格的设置】--------------------------------------------------
  39. //  说明:设定GUI风格
  40. //------------------------------------------------------------------------------------------------------------------
  41. private static GUIStyle Style
  42. {
  43. get
  44. {
  45. if (style == null)
  46. {
  47. //新建一个largeLabel的GUI风格
  48. style = new GUIStyle(EditorStyles.largeLabel);
  49. //设置文本居中对齐
  50. style.alignment = TextAnchor.MiddleCenter;
  51. //设置GUI的文本颜色
  52. style.normal.textColor = new Color(0.9f, 0.9f, 0.9f);
  53. //设置GUI的文本字体大小
  54. style.fontSize = 26;
  55. }
  56. return style;
  57. }
  58. }
  59. //-----------------------------------------【OnGUI()函数】-----------------------------------------------------
  60. // 说明:游戏执行时GUI的显示
  61. //------------------------------------------------------------------------------------------------------------------
  62. void OnGUI( )
  63. {
  64. //ShowInfoInGamePlay为真时,才进行绘制
  65. if (ShowInfoInGamePlay)
  66. {
  67. //---------------------------------【1.光线投射推断&计算位置坐标】-------------------------------
  68. //定义一条射线
  69. Ray ray = new Ray(transform.position + TargetCamera.transform.up * 6f, -TargetCamera.transform.up);
  70. //定义光线投射碰撞
  71. RaycastHit raycastHit;
  72. //进行光线投射操作,第一个參数为光线的開始点和方向。第二个參数为光线碰撞器碰到哪里的输出信息,第三个參数为光线的长度
  73. collider.Raycast(ray, out raycastHit, Mathf.Infinity);
  74. //计算距离。为当前摄像机位置减去碰撞位置的长度
  75. float distance = (TargetCamera.transform.position - raycastHit.point).magnitude;
  76. //设置字体大小。在26到12之间插值
  77. float fontSize = Mathf.Lerp(26, 12, distance / 10f);
  78. //将得到的字体大小赋给Style.fontSize
  79. Style.fontSize = (int)fontSize;
  80. //将文字位置取为得到的光线碰撞位置上方一点
  81. Vector3 worldPositon = raycastHit.point + TargetCamera.transform.up * distance * 0.03f;
  82. //世界坐标转屏幕坐标
  83. Vector3 screenPosition = TargetCamera.WorldToScreenPoint(worldPositon);
  84. //z坐标值的推断,z值小于零就返回
  85. if (screenPosition.z <= 0){return;}
  86. //翻转Y坐标值
  87. screenPosition.y = Screen.height - screenPosition.y;
  88. //获取文本尺寸
  89. Vector2 stringSize = Style.CalcSize(new GUIContent(text));
  90. //计算文本框坐标
  91. Rect rect = new Rect(0f, 0f, stringSize.x + 6, stringSize.y + 4);
  92. //设定文本框中心坐标
  93. rect.center = screenPosition - Vector3.up * rect.height * 0.5f;
  94. //----------------------------------【2.GUI绘制】---------------------------------------------
  95. //開始绘制一个简单的文本框
  96. Handles.BeginGUI();
  97. //绘制灰底背景
  98. GUI.color = new Color(0f, 0f, 0f, 0.8f);
  99. GUI.DrawTexture(rect, EditorGUIUtility.whiteTexture);
  100. //绘制文字
  101. GUI.color = new Color(1, 1, 1, 0.8f);
  102. GUI.Label(rect, text, Style);
  103. //结束绘制
  104. Handles.EndGUI();
  105. }
  106. }
  107. //-------------------------------------【OnDrawGizmos()函数】---------------------------------------------
  108. // 说明:场景编辑器中GUI的显示
  109. //------------------------------------------------------------------------------------------------------------------
  110. void OnDrawGizmos()
  111. {
  112. //ShowInfoInSeneEditor为真时,才进行绘制
  113. if (ShowInfoInSceneEditor)
  114. {
  115. //----------------------------------------【1.光线投射推断&计算位置坐标】----------------------------------
  116. //定义一条射线
  117. Ray ray = new Ray(transform.position + Camera.current.transform.up * 6f, -Camera.current.transform.up);
  118. //定义光线投射碰撞
  119. RaycastHit raycastHit;
  120. //进行光线投射操作,第一个參数为光线的開始点和方向,第二个參数为光线碰撞器碰到哪里的输出信息。第三个參数为光线的长度
  121. collider.Raycast(ray, out raycastHit, Mathf.Infinity);
  122. //计算距离,为当前摄像机位置减去碰撞位置的长度
  123. float distance = (Camera.current.transform.position - raycastHit.point).magnitude;
  124. //设置字体大小。在26到12之间插值
  125. float fontSize = Mathf.Lerp(26, 12, distance / 10f);
  126. //将得到的字体大小赋给Style.fontSize
  127. Style.fontSize = (int)fontSize;
  128. //将文字位置取为得到的光线碰撞位置上方一点
  129. Vector3 worldPositon = raycastHit.point + Camera.current.transform.up * distance * 0.03f;
  130. //世界坐标转屏幕坐标
  131. Vector3 screenPosition = Camera.current.WorldToScreenPoint(worldPositon);
  132. //z坐标值的推断,z值小于零就返回
  133. if (screenPosition.z <= 0) { return; }
  134. //翻转Y坐标值
  135. screenPosition.y = Screen.height - screenPosition.y;
  136. //获取文本尺寸
  137. Vector2 stringSize = Style.CalcSize(new GUIContent(text));
  138. //计算文本框坐标
  139. Rect rect = new Rect(0f, 0f, stringSize.x + 6, stringSize.y + 4);
  140. //设定文本框中心坐标
  141. rect.center = screenPosition - Vector3.up * rect.height * 0.5f;
  142. //----------------------------------【2.GUI绘制】---------------------------------------------
  143. //開始绘制一个简单的文本框
  144. Handles.BeginGUI();
  145. //绘制灰底背景
  146. GUI.color = new Color(0f, 0f, 0f, 0.8f);
  147. GUI.DrawTexture(rect, EditorGUIUtility.whiteTexture);
  148. //绘制文字
  149. GUI.color = new Color(1, 1, 1, 0.8f);
  150. GUI.Label(rect, text, Style);
  151. //结束绘制
  152. Handles.EndGUI();
  153. }
  154. }
  155. }
  156. //预编译命令结束
  157. #endif

这个脚本的使用方法倒是非常easy,在代码的说明部分已经具体写出,在这里我们再列出一遍:

第一步:在Unity中拖拽此脚本到某物体之上,或在Inspector中[Add Component]->[浅墨's Toolkit v1.0]->[ShowObjectInfo]

第二步:在Inspector里,ShowObject Info 栏中的TargetCamera參数中选择需面向的摄像机,如Main Camera。FirstPerson Controller等

第三步:在text參数里填须要显示输出的文字。

第四步:完毕。执行游戏或在场景编辑器Scene中查看显示效果。

也就是拖拽ShowObjectInfo脚本或者直接加入组件给须要附加文字的物体,然后在Inspector中输入须要显示的文字。然后选择其面对的摄像机就能够了。

我们将ShowObjectInfo脚本拖拽给上文中刚刚变得炫酷外形黑科技的Capsule:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

那么他在Inspector就多了一个“ShowObject Info(Script)”组件,将该组件的Text项中填上“凹凸纹理+边缘发光效果”,TargetCamera中填上First Person Controller的子物体Main Camera:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

最后,得到的效果就是这样:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

五、总结、配套资源&终于project下载

好了,本篇的文章到这里就大概结束了。

今天讲的内容还是非常多的,对于新接触Unity的朋友们来说也许还得好好消化消化,而熟悉Unity的朋友应该非常快就能够看懂,或者认为浅墨讲了一堆废话,orz。

这篇文章的内容说白了就很easy。也就是新建project,然后导入三个浅墨提前准备好的unitypackage游戏资源,点一点鼠标拖动拖动脚本。新建一个Shader。写点代码,再创建一个Material,Shader赋给这个Material,最后创建一个胶囊状Capsule。Material赋给这个Capsule,点执行查看终于效果。一切,就是这么简单。

:)

本文配套的unitypackage请点击此处下载:

本文终于的Unityproject请点击此处下载:

最后放几张终于的场景美图吧。

站在亭子上看世界:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9lbV9xaWFubW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

逼真的光晕:

美丽的天空:

乱真的水面:

蓝天和草地树木交相辉映:

OK,全文到此结束。

新的游戏编程之旅已经开启,下周一,我们不见不散。

很多其它文章尽在:http://www.hpw123.net

版权声明:本文博客原创文章。博客,未经同意,不得转载。

【淡墨Unity3D Shader计划】一间 创建一个游戏场景 &amp; 第一Shader写作的更多相关文章

  1. three.js-走进3d的奇妙世界一创建一个三维场景

      一.git代码仓库地址 git clone https://github.com/josdirksen/learning-threejs-third  下载并解压 二.创建一个三维场景 如下图所示 ...

  2. 使用Unity3D的设计思想实现一个简单的C#赛车游戏场景

    最近看了看一个C#游戏开发的公开课,在该公开课中使用面向对象思想与Unity3D游戏开发思想结合的方式,对一个简单的赛车游戏场景进行了实现.原本在C#中很方便地就可以完成的一个小场景,使用Unity3 ...

  3. 【淡墨Unity3D Shader计划】四 热带雨林的文章: 排除、深度测试、Alpha测试和基本雾编译

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://hpw123.net/a/C__/kongzhitaichengxu/2014/1222/163.html 作者:毛星云 ...

  4. 【浅墨Unity3D Shader编程】之中的一个 夏威夷篇:游戏场景的创建 &amp; 第一个Shader的书写

    本系列文章由@浅墨_毛星云 出品.转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40723789 作者:毛星云(浅墨)  ...

  5. UE4编程之C++创建一个FPS工程(一)创建模式&角色&处理输入

    转自:http://blog.csdn.net/u011707076/article/details/44180951 从今天开始,我们一起来学习一下,如何使用C++将一个不带有任何初学者内容的空模板 ...

  6. Cocos2d-android (01) 创建一个简单的cocos2d应用程序

    下载Cocos2d-android的源代码:cocos2d-android-1 git@github.com:ZhouWeikuan/cocos2d.git 将项目导入到eclipse中.运行实例: ...

  7. java操作Excel的poi 创建一个sheet页

    package com.java.poi; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.us ...

  8. 使用IDEA创建一个Servlet应用程序

    使用IDEA创建一个Servlet应用程序 第一步:创建web应用 选择web application应用,之后填写项目名称等. 第二步:项目配置 在WEB-INF目录下创建两个文件夹:classes ...

  9. Unity3D研究院之异步加载游戏场景与异步加载游戏资源进度条

    Unity3D研究院之异步加载游戏场景与异步加载游戏资源进度条 异步任务相信大家应该不会陌生,那么本章内容MOMO将带领大家学习Unity中的一些异步任务.在同步加载游戏场景的时候通常会使用方法 Ap ...

随机推荐

  1. poj 2126 Factoring a Polynomial 数学多项式分解

    题意: 给一个多项式,求它在实数域内的可分解性. 分析: 代数基本定理. 代码: //poj 2126 //sep9 #include <iostream> using namespace ...

  2. find-a-jar-file-given-the-class-name

    Save this as findclass.sh (or whatever), put it on your path and make it executable: #!/bin/sh find ...

  3. 4. Qt的容器类

      Qt提供来一组通用的基于模板的容器类. 一. QList类,QLinkedList类 和  QVector类         QList类.QLinkedList类和QVector类常常使用到的Q ...

  4. NumPy简明教程

    源地址:http://blog.csdn.net/sunny2038/article/details/9002531 http://blog.csdn.net/sunny2038/article/de ...

  5. Delphi中复制带有String的记录结构时不能使用Move之类的内存操作函数

    请看下面的代码: program TestRecord; {$APPTYPE CONSOLE} uses  SysUtils,  Math; type  TRecordA = record    Na ...

  6. DLNA_百度百科

    DLNA_百度百科 DLNA

  7. 深入认识Tigase XMPP Server(上)

    深入认识Tigase XMPP Server(上) 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 本文的目的是深入认识Tigase XMPP Serve ...

  8. NET Core RC2

    .NET Core RC2/RTM 明确了时间表 .NET Core 经过了将近2年的开发,去年12月份发布的RC1版本,明确来说那只是一个beta版本,自从RC1发布以来,看到github里的RC2 ...

  9. PHP网站安装程序的原理及代码

    原文:PHP网站安装程序的原理及代码 原理: 其实PHP程序的安装原理无非就是将数据库结构和内容导入到相应的数据库中,从这个过程中重新配置连接数据库的参数和文件,为了保证不被别人恶意使用安装文件,当安 ...

  10. Java NIO 完全学习笔记(转)

    本篇博客依照 Java NIO Tutorial翻译,算是学习 Java NIO 的一个读书笔记.建议大家可以去阅读原文,相信你肯定会受益良多. 1. Java NIO Tutorial Java N ...