您需要 登录 才可以下载或查看,没有账号?注册
x
接上文:http://www.element3ds.com/forum.php?mod=viewthread&tid=52290
【浅墨Unity3D Shader编程系列-第一篇--夏威夷】 PS:由于原文较长.特分为上下两篇.作者原话(原帖地址见末尾):
[文章所带资源.回复可见.]
作为一个系统介绍unity3D中Shader编写的系列文章的开篇,本文的第一部分为系列文章的前言,
然后第二部分介绍了这个系列文章中我们会使用的游戏场景创建方式,
最后一部分讲解了如何在Unity中创建和使用Shader,
为后面专注于介绍如何在Unity中进行Shader编程打好了基础。
四、书写和使用第一个Shader
上文讲解的都是一般的场景构建过程,接下来就要正式开始我们的核心部分,书写第一个Shader了。在完成上文中讲解的创建好漂亮的场景之后,
我们首先可以在Assets根目录下创建一个文件夹,用于以后Shader和Material文件的存放。
创建过程可以是在Project面板的空白处右键->Create->Folder,也可以是点击Project面板中的Create下拉菜单->Folder
给新出来的这个文件夹取名Shaders,然后回车。
然后再用同样的方法在Assets根目录下创建一个名为Textures的文件夹,
用于稍后素材图片的存放。那么,如果你按照浅墨按照目前描述的步骤来的话,Assets根目录到现在就是这样的5个文件夹:
接着,进去到我们创建的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已经为我们写好了很多代码。
我们不妨自己重新写点不一样的东西。删掉原本的这些代码,拷贝浅墨写的如下代码到编辑器中:
- //-----------------------------------------------【Shader说明】----------------------------------------------
- // Shader功能: 凹凸纹理显示+自选边缘颜色和强度
- // 使用语言: Shaderlab
- // 开发所用IDE版本:Unity4.5 06f 、Monodevelop
- // 2014年11月2日 Created by 浅墨
- // 更多内容或交流请访问浅墨的博客:[url=http://blog.csdn.net/poem_qianmo]http://blog.csdn.net/poem_qianmo[/url]
- //---------------------------------------------------------------------------------------------------------------------
-
-
- Shader "浅墨Shader编程/0.TheFirstShader"
- {
- //-------------------------------【属性】-----------------------------------------
- Properties
- {
- _MainTex ("【纹理】Texture", 2D) = "white" {}
- _BumpMap ("【凹凸纹理】Bumpmap", 2D) = "bump" {}
- _RimColor ("【边缘颜色】Rim Color", Color) = (0.17,0.36,0.81,0.0)
- _RimPower ("【边缘颜色强度】Rim Power", Range(0.6,9.0)) = 1.0
- }
-
- //----------------------------【开始一个子着色器】---------------------------
- SubShader
- {
- //渲染类型为Opaque,不透明
- Tags { "RenderType" = "Opaque" }
-
- //-------------------开始CG着色器编程语言段-----------------
- CGPROGRAM
-
- //使用兰伯特光照模式
- #pragma surface surf Lambert
-
- //输入结构
- struct Input
- {
- float2 uv_MainTex;//纹理贴图
- float2 uv_BumpMap;//法线贴图
- float3 viewDir;//观察方向
- };
-
- //变量声明
- sampler2D _MainTex;//主纹理
- sampler2D _BumpMap;//凹凸纹理
- float4 _RimColor;//边缘颜色
- float _RimPower;//边缘颜色强度
-
- //表面着色函数的编写
- void surf (Input IN, inout SurfaceOutput o)
- {
- //表面反射颜色为纹理颜色
- o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
- //表面法线为凹凸纹理的颜色
- o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
- //边缘颜色
- half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
- //边缘颜色强度
- o.Emission = _RimColor.rgb * pow (rim, _RimPower);
- }
-
- //-------------------结束CG着色器编程语言段------------------
- ENDCG
- }
-
- //“备胎”为普通漫反射
- Fallback "Diffuse"
- }
点击此处复制文本
由于这是第一篇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包中,同样双击这个包然后打开导入到项目中。
我们在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,其详细注释的代码如下:
- //-----------------------------------------------【脚本说明】------------------------------------------------------- [/p][list=1]
-
-
- // 脚本功能: 在场景中和游戏窗口中分别显示给任意物体附加的文字标签信息
- // 使用语言: C#
- // 开发所用IDE版本:Unity4.5 06f 、Visual Studio 2010
- // 2014年10月 Created by 浅墨
- // 更多内容或交流,请访问浅墨的博客:[url=http://blog.csdn.net/poem_qianmo]http://blog.csdn.net/poem_qianmo[/url]
- //---------------------------------------------------------------------------------------------------------------------
-
- //-----------------------------------------------【使用方法】-------------------------------------------------------
- // 第一步:在Unity中拖拽此脚本到某物体之上,或在Inspector中[Add Component]->[浅墨's Toolkit v1.0]->[ShowObjectInfo]
- // 第二步:在Inspector里,Show Object Info 栏中的TargetCamera参数中选择需面向的摄像机,如MainCamera
- // 第三步:在text参数里填需要显示输出的文字。
- // 第四步:完成。运行游戏或在场景编辑器Scene中查看显示效果。
-
- // PS:默认情况下文本信息仅在游戏运行时显示。
- // 若需要在场景编辑时在Scene中显示,请勾选Show Object Info 栏中的[Show Info In Scene Editor]参数。
- // 同理,勾选[Show Info In Game Play]参数也可以控制是否在游戏运行时显示文本信息
- //---------------------------------------------------------------------------------------------------------------------
-
-
- //预编译指令,检测到UNITY_EDITOR的定义,则编译后续代码
- #if UNITY_EDITOR
-
-
- //------------------------------------------【命名空间包含部分】----------------------------------------------------
- // 说明:命名空间包含
- //----------------------------------------------------------------------------------------------------------------------
- using UnityEngine;
- using UnityEditor;
- using System.Collections;
-
- //添加组件菜单
- [AddComponentMenu("浅墨's Toolkit v1.0/ShowObjectInfo")]
-
-
- //开始ShowObjectInfo类
- public class ShowObjectInfo : MonoBehaviour
- {
-
- //------------------------------------------【变量声明部分】----------------------------------------------------
- // 说明:变量声明部分
- //------------------------------------------------------------------------------------------------------------------
- public string text="键入你自己的内容 by浅墨";//文本内容
- public Camera TargetCamera;//面对的摄像机
- public bool ShowInfoInGamePlay = true;//是否在游戏运行时显示此信息框的标识符
- public bool ShowInfoInSceneEditor = false;//是否在场景编辑时显示此信息框的标识符
- private static GUIStyle style;//GUI风格
-
-
-
- //------------------------------------------【GUI 风格的设置】--------------------------------------------------
- // 说明:设定GUI风格
- //------------------------------------------------------------------------------------------------------------------
- private static GUIStyle Style
- {
- get
- {
- if (style == null)
- {
- //新建一个largeLabel的GUI风格
- style = new GUIStyle(EditorStyles.largeLabel);
- //设置文本居中对齐
- style.alignment = TextAnchor.MiddleCenter;
- //设置GUI的文本颜色
- style.normal.textColor = new Color(0.9f, 0.9f, 0.9f);
- //设置GUI的文本字体大小
- style.fontSize = 26;
- }
- return style;
- }
-
- }
-
-
-
-
- //-----------------------------------------【OnGUI()函数】-----------------------------------------------------
- // 说明:游戏运行时GUI的显示
- //------------------------------------------------------------------------------------------------------------------
- void OnGUI( )
- {
- //ShowInfoInGamePlay为真时,才进行绘制
- if (ShowInfoInGamePlay)
- {
- //---------------------------------【1.光线投射判断&计算位置坐标】-------------------------------
- //定义一条射线
- Ray ray = new Ray(transform.position + TargetCamera.transform.up * 6f, -TargetCamera.transform.up);
- //定义光线投射碰撞
- RaycastHit raycastHit;
- //进行光线投射操作,第一个参数为光线的开始点和方向,第二个参数为光线碰撞器碰到哪里的输出信息,第三个参数为光线的长度
- collider.Raycast(ray, out raycastHit, Mathf.Infinity);
-
- //计算距离,为当前摄像机位置减去碰撞位置的长度
- float distance = (TargetCamera.transform.position - raycastHit.point).magnitude;
- //设置字体大小,在26到12之间插值
- float fontSize = Mathf.Lerp(26, 12, distance / 10f);
- //将得到的字体大小赋给Style.fontSize
- Style.fontSize = (int)fontSize;
- //将文字位置取为得到的光线碰撞位置上方一点
- Vector3 worldPositon = raycastHit.point + TargetCamera.transform.up * distance * 0.03f;
- //世界坐标转屏幕坐标
- Vector3 screenPosition = TargetCamera.WorldToScreenPoint(worldPositon);
- //z坐标值的判断,z值小于零就返回
- if (screenPosition.z <= 0){return;}
- //翻转Y坐标值
- screenPosition.y = Screen.height - screenPosition.y;
-
- //获取文本尺寸
- Vector2 stringSize = Style.CalcSize(new GUIContent(text));
- //计算文本框坐标
- Rect rect = new Rect(0f, 0f, stringSize.x + 6, stringSize.y + 4);
- //设定文本框中心坐标
- rect.center = screenPosition - Vector3.up * rect.height * 0.5f;
-
-
- //----------------------------------【2.GUI绘制】---------------------------------------------
- //开始绘制一个简单的文本框
- Handles.BeginGUI();
- //绘制灰底背景
- GUI.color = new Color(0f, 0f, 0f, 0.8f);
- GUI.DrawTexture(rect, EditorGUIUtility.whiteTexture);
- //绘制文字
- GUI.color = new Color(1, 1, 1, 0.8f);
- GUI.Label(rect, text, Style);
- //结束绘制
- Handles.EndGUI();
- }
- }
-
- //-------------------------------------【OnDrawGizmos()函数】---------------------------------------------
- // 说明:场景编辑器中GUI的显示
- //------------------------------------------------------------------------------------------------------------------
- void OnDrawGizmos()
- {
- //ShowInfoInSeneEditor为真时,才进行绘制
- if (ShowInfoInSceneEditor)
- {
- //----------------------------------------【1.光线投射判断&计算位置坐标】----------------------------------
- //定义一条射线
- Ray ray = new Ray(transform.position + Camera.current.transform.up * 6f, -Camera.current.transform.up);
- //定义光线投射碰撞
- RaycastHit raycastHit;
- //进行光线投射操作,第一个参数为光线的开始点和方向,第二个参数为光线碰撞器碰到哪里的输出信息,第三个参数为光线的长度
- collider.Raycast(ray, out raycastHit, Mathf.Infinity);
-
- //计算距离,为当前摄像机位置减去碰撞位置的长度
- float distance = (Camera.current.transform.position - raycastHit.point).magnitude;
- //设置字体大小,在26到12之间插值
- float fontSize = Mathf.Lerp(26, 12, distance / 10f);
- //将得到的字体大小赋给Style.fontSize
- Style.fontSize = (int)fontSize;
- //将文字位置取为得到的光线碰撞位置上方一点
- Vector3 worldPositon = raycastHit.point + Camera.current.transform.up * distance * 0.03f;
- //世界坐标转屏幕坐标
- Vector3 screenPosition = Camera.current.WorldToScreenPoint(worldPositon);
- //z坐标值的判断,z值小于零就返回
- if (screenPosition.z <= 0) { return; }
- //翻转Y坐标值
- screenPosition.y = Screen.height - screenPosition.y;
-
- //获取文本尺寸
- Vector2 stringSize = Style.CalcSize(new GUIContent(text));
- //计算文本框坐标
- Rect rect = new Rect(0f, 0f, stringSize.x + 6, stringSize.y + 4);
- //设定文本框中心坐标
- rect.center = screenPosition - Vector3.up * rect.height * 0.5f;
-
-
-
- //----------------------------------【2.GUI绘制】---------------------------------------------
- //开始绘制一个简单的文本框
- Handles.BeginGUI();
- //绘制灰底背景
- GUI.color = new Color(0f, 0f, 0f, 0.8f);
- GUI.DrawTexture(rect, EditorGUIUtility.whiteTexture);
- //绘制文字
- GUI.color = new Color(1, 1, 1, 0.8f);
- GUI.Label(rect, text, Style);
- //结束绘制
- Handles.EndGUI();
-
- }
-
- }
-
- }
-
- //预编译命令结束
- #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,点运行查看最终效果。一切,就是这么简单。
附上几张使用shader的美图:
文章配套的三个unitypackage和最终的工程源码回复可见
尊敬的 游客,如果您要查看本帖关注 或 回复可见内容请 关注或 回复后刷新页面查看!
原文链接: http://blog.csdn.net/poem_qianmo/article/details/40723789
|