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

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

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

邮箱: happylifemxy@163.com

    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. #pragma surface surf Lambert
    27. //输入结构
    28. struct Input
    29. {
    30. float2 uv_MainTex;//纹理贴图
    31. float2 uv_BumpMap;//法线贴图
    32. float3 viewDir;//观察方向
    33. };
    34. //变量声明
    35. sampler2D _MainTex;//主纹理
    36. sampler2D _BumpMap;//凹凸纹理
    37. float4 _RimColor;//边缘颜色
    38. float _RimPower;//边缘颜色强度
    39. //表面着色函数的编写
    40. void surf (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. half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));  //saturate 把输入的值限制在0到1之间
    48. //边缘颜色强度
    49. o.Emission = _RimColor.rgb * pow (rim, _RimPower);  //pow(x,y)x的y次方
    50. }
    51. //-------------------结束CG着色器编程语言段------------------
    52. ENDCG
    53. }
    54. //“备胎”为普通漫反射
    55. Fallback "Diffuse"
    56. }

由于这是第一篇Shader系列文章,已经涉及到很多内容了,所以浅墨不可能展开讲解这段代码的具体思路和写法,不过已经详细注释,大家应该会多少有点明白。随着稍后文章的深入,这段代码就显得很简单易懂了。

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

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

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

知道以上原理了就很简单了,在“0.TheFirstShader.shader”的同一目录下创建一个Material。同样是可以通过Create下拉菜单->Material或者空白处右键->create->Material来完成。

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

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

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

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

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

我们在Textures文件夹下找到这两张纹理,接下来做的就是将他们拖动到0.TheFirstShader材质对应的纹理区域中,如下:

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

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

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

这个脚本的用法倒是很简单,在代码的说明部分已经详细写出,在这里我们再列出一遍:

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

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

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

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

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

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

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

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

五、总结、配套资源&最终工程下载

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

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

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

本文配套的三个unitypackage打包请点击此处下载:

【浅墨Unity3D Shader编程】之一 配套的三个unitypackage打包下载

本文最终的Unity工程请点击此处下载:

【浅墨Unity3D Shader编程】之一 配套Unity工程

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

站在亭子上看世界:

逼真的光晕:

漂亮的天空:

乱真的水面:

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

OK,全文到此结束。

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

shader 编程入门(一)的更多相关文章

  1. Unity3D着色器Shader编程入门(一)

    自学Unity3D也有大半年了,对Shader一直不敢入坑,最近看了些资料,以及通过自己的实践,对Shader还是有一点了解了,分享下仅作入门参考. 因Shader是对图像图像渲染的,学习前可以去了解 ...

  2. 分形的奥秘!分形着色器!shader 编程入门实战 ! Cocos Creator!

    极致的数学之美! 什么是分形? "一个粗糙或零碎的几何形状,可以分成数个部分,且每一部分都(至少近似地)是整体缩小后的形状" 简单来说,分形(fractal)就像这个doge表情包 ...

  3. Shader 简明入门教程

    Unity3D的所有渲染工作都离不开着色器(Shader),如果你和我一样最近开始对Shader编程比较感兴趣的话,可能你和我有着同样的困惑:如何开始?Unity3D提供了一些Shader的手册和文档 ...

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

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

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

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

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

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

  7. PHP面向对象(OOP)编程入门教程

    面向对象编程(OOP)是我们编程的一项基本技能,PHP5对OOP提供了良好的支持.如何使用OOP的思想来进行PHP的高级编程,对于提高 PHP编程能力和规划好Web开发构架都是非常有意义的.下面我们就 ...

  8. Windows编程入门程序详解

    引用:http://blog.csdn.net/jarvischu/article/details/8115390 1.     程序 /******************************* ...

  9. 【PHP面向对象(OOP)编程入门教程】1.什么是面向对象?

    面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计)是一种计算机编程架构,OOP的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成 ...

随机推荐

  1. 第六章在U盘上运行openwrt(引导)--补

    1.前言 前面已经把U盘挂在了703N上了,现在只需要打开路由器,使用TTL串口或者putty(ssh模式需要用户名和密码-第一章刷openwrt的时候已经设置好)登陆路由器. 2.将系统内所有文件同 ...

  2. 《Word排版艺术》读后感,兼谈LaTeX

    我有两年多的LaTeX使用经验,用它排实验报告.毕业论文和书籍(半本):Word的使用时间长一些,但真正用好也不过是近一两年的事.这两个软件我都 用得很熟,我想我可以一边谈谈读<Word排版艺术 ...

  3. 二十四种设计模式:观察者模式(Observer Pattern)

    观察者模式(Observer Pattern) 介绍定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新. 示例有一个Message实体类,某些对象 ...

  4. 二十四种设计模式:访问者模式(Visitor Pattern)

    访问者模式(Visitor Pattern) 介绍表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 示例有一个Message实体类,某些对象对 ...

  5. [转]How to solve SSIS error code 0xC020801C/0xC004700C/0xC0047017

    本文转自:http://www.codeproject.com/Articles/534651/HowplustoplussolveplusSSISpluserrorpluscodeplus0xC B ...

  6. [转]create a basic sql server 2005 trigger to send email alerts

    本文转自:http://blog.netnerds.net/2008/02/create-a-basic-sql-server-2005-trigger-to-send-e-mail-alerts/ ...

  7. [React + Functional Programming ADT] Create Redux Middleware to Dispatch Multiple Actions

    We only have a few dispatching functions that need to be known by our React Application. Each one ac ...

  8. Java中equals()、equalsIgnoreCase()和==的区别

    用久了C#,在Java中,判断一个字符串还是习惯性的用了==,但是总是不能按照正确的判断分支运行,后来才想起来Java中是有equals的,然后就有引出了equalsIgnoreCase.   这三种 ...

  9. ListView改变字体

    ListView中没有改变字体的属性和方法,所以需要用其他的方式.这里使用ArrayAdapter.   首先,创建一个TextView组件,用来作为Adapter的列表项组件.   在layout文 ...

  10. Mach-O文件格式和程序从载入到运行过程

    > 之前深入了解过.过去了一年多的时间.如今花些时间好好总结下,毕竟好记性不如烂笔头. 其次另一个目的,对于mach-o文件结构.关于动态载入信息那个数据区中,命令含义没有深刻掰扯清除,希望有同 ...