1. 绪论

最近想学习一下Unity3d,无奈发现现在大部分教程不仅是视频形式的,面对的也是美术、设计之类的非程序员,更多的时候都是把Unity3d当作PS一样的工具来用,真正面对程序开发的教程反而非常少,更不用说希望能研究到一些底层图形技术的技术工作者了。

说一下我看的两本Unity3d书籍吧。第一本是《Unity 3D游戏开发(第2版)》(宣雨松 著)。这本书算是大部分教程书籍中评价比较好的了,很多人推荐。不过个人感觉作者对Unity3D的知识有了太多的积累,已经忘记了初学者初学Unity3D的心态,知识也显得比较零散。不过这也是国内大多数书籍的通病了,更像是作者对知识的总结而不是成体系的向读者介绍知识。建议初学者看这本书一定要实操,喜欢头脑风暴的同学不适合这本书。

看的第二本书是《Unity Shader入门精要》(冯乐乐 著)。令人佩服的是这本书是位程序媛写的,可能正是因为如此,这本书写的确实非常细致到位。尤其是前面几章对渲染管线的描述,从Unity3D图形技出发,已然上升到计算机图形学的高度上,对学习其他的图形技术也有非常大的帮助(毕竟很多图形技术都是通用的)。当时看了觉得确实很不错,因此还送了同事一本。

最后就是自己也想总结一下Unity3D的相关知识吧,本身是个程序猿,当然更多的会偏向游戏开发的程序员角度,或者图形技术的程序员的角度一点。

2. 概述

图形渲染技术的第一个HelloWorld当然应该就是绘制一个三角形了。在绝大多数情况下,三角面是渲染物体的基础图元。作为高级的渲染引擎,像三角面这样的几何体甚至不需要我们去通过代码来绘制,但是却是我们学习的基础,立足于这个基础,我们以后能够渲染更加复杂的图形。

3. 详论

3.1. 准备

通过Unity Hub创建一个3D工程:

进入Unity3D环境,通过右键菜单,在"Hierarchy"视图中添加一个名为"Root"空的GameObject:

GameObject对象是Unity3D中得一个基础类,Unity3D中得绝大部分对象都是基于它实现的,比如相机、灯光、或者模型等。所以我们这里把创建的名为Root的GameObject对象作为场景的根节点。

在Root对象的Inspector面板中,可以看到一个"Add Component"按钮:

也就是说,通过"Add Component"按钮,我们可以挂接一些组件,这样,空的GameObject对象就成为了其他类型的对象。例如,我这里挂接一个C#脚本,通过C#脚本来绘制物体,那么这个GameObject,表示的就是一个渲染的物体。

在"Project"视图中,通过右键菜单创建一个C#脚本:

通过Root对象的Inspector面板中的"Add Component"按钮,将这个脚本,挂接到Root对象下:

3.2. 实现

通过"Project"视图的右键菜单中打开这个C#工程,可以看到我们添加的脚本"Main.CS":

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Main : MonoBehaviour
  5. {
  6. // Start is called before the first frame update
  7. void Start()
  8. {
  9. }
  10. // Update is called once per frame
  11. void Update()
  12. {
  13. }
  14. }

这个脚本提供了两个方法:

  1. Start()表示初始化(第一帧)的时候需要更新的内容,通常用于初始化之后不再更新的内容。比如我们会在这里绘制一个物体。
  2. Update()表示每一帧都需要实时更新的内容,比如相机与鼠标键盘事件的交互。

那么就在Start()中进行绘制一个三角形的操作:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Main : MonoBehaviour
  5. {
  6. // Start is called before the first frame update
  7. void Start()
  8. {
  9. GameObject main = GameObject.Find("/Root");
  10. if (main == null)
  11. {
  12. return;
  13. }
  14. GameObject triangleGameObject = GreateTriangle();
  15. triangleGameObject.transform.parent = main.transform;
  16. }
  17. GameObject GreateTriangle()
  18. {
  19. string name = "triangle";
  20. Mesh mesh = new Mesh();
  21. mesh.name = name;
  22. Vector3[] vertices = new Vector3[3]
  23. {
  24. new Vector3(0, 0, 0),
  25. new Vector3(0, 10, 0),
  26. new Vector3(10, 0, 0)
  27. };
  28. mesh.vertices = vertices;
  29. int[] triangles = new int[3] { 0, 1, 2 };
  30. mesh.triangles = triangles;
  31. GameObject triangleGameObject = new GameObject(name);
  32. MeshFilter mf = triangleGameObject.AddComponent<MeshFilter>();
  33. mf.sharedMesh = mesh;
  34. MeshRenderer meshRenderer = triangleGameObject.AddComponent<MeshRenderer>();
  35. return triangleGameObject;
  36. }
  37. // Update is called once per frame
  38. void Update()
  39. {
  40. }
  41. }

3.3. 解析

3.3.1. 场景树对象

在Start()函数中,首先我们找到了场景根节点Root,然后又通过调用GreateTriangle()函数,创建了一个三角形的GameObject对象,最后把这个三角形对象挂接到Root对象下:

  1. void Start()
  2. {
  3. GameObject main = GameObject.Find("/Root");
  4. if (main == null)
  5. {
  6. return;
  7. }
  8. GameObject triangleGameObject = GreateTriangle();
  9. triangleGameObject.transform.parent = main.transform;
  10. }

可以看到子对象挂接到父对象是通过GameObject对象中Transform对象来挂接的,这其实体现了一种思维的体现:Transform其实是表达GameObject对象空间位置的的4X4矩阵,父节点设置Transform会影响到子节点的位置,子节点的初始位置都是基于父节点的Transform开始计算的。通过这种方式,再复杂的场景也可以组织成一个场景树节点:

3.3.2. 绘制方法

经过图形技术的多年发展,现在大部分图像渲染引擎都会把渲染的物体封装成两种对象:渲染物体的骨架封装成Mesh(网格),因为绝大多数物体都是通过一个个三角面片渲染出来的;渲染物体的血肉封装成Material(材质),影响最终渲染的效果,如物体的光感、质地。

所以,为了绘制一个三角形,当然应该先绘制一个Mesh:

  1. GameObject GreateTriangle()
  2. {
  3. string name = "triangle";
  4. Mesh mesh = new Mesh();
  5. mesh.name = name;
  6. Vector3[] vertices = new Vector3[3]
  7. {
  8. new Vector3(0, 0, 0),
  9. new Vector3(0, 10, 0),
  10. new Vector3(10, 0, 0)
  11. };
  12. mesh.vertices = vertices;
  13. int[] triangles = new int[3] { 0, 1, 2 };
  14. mesh.triangles = triangles;
  15. //...
  16. }

这里,我们给Mesh传入了三个顶点,以及顶点的三角面索引。三角面索引表示的是按照索引的顺序,通过顶点进行绘制,这样就可以使用较少的顶点进行绘制,节约空间,毕竟Mesh中很多三角面片是共顶点的。

接下来,给GameObject增加一个MeshFilter组件,通过这个组件挂接刚创建的Mesh;给GameObject增加一个MeshRenderer组件,这个组件是用来挂接Material的,不过暂时没有用上Material(但是必须增加MeshRenderer组件,否则不会显示物体)。

  1. GameObject GreateTriangle()
  2. {
  3. //...
  4. GameObject triangleGameObject = new GameObject(name);
  5. MeshFilter mf = triangleGameObject.AddComponent<MeshFilter>();
  6. mf.sharedMesh = mesh;
  7. MeshRenderer meshRenderer = triangleGameObject.AddComponent<MeshRenderer>();
  8. return triangleGameObject;
  9. }

4. 结果

点击"Play",运行结果如下:



Unity3D学习笔记1——绘制一个三角形的更多相关文章

  1. Unity3D学习笔记2——绘制一个带纹理的面

    目录 1. 概述 2. 详论 2.1. 网格(Mesh) 2.1.1. 顶点 2.1.2. 顶点索引 2.2. 材质(Material) 2.2.1. 创建材质 2.2.2. 使用材质 2.3. 光照 ...

  2. OpenGL学习笔记(1) 画一个三角形

    最近找实习有一丢丢蛋疼,沉迷鬼泣5,四周目通关,又不想写代码,写篇笔记复习一下,要好好学图形学啊 用OpenGL画一个三角形 项目的简介 记录一下跟着learnOpenGL学习的过程 笔记里的代码放在 ...

  3. Unity3D学习笔记3——Unity Shader的初步使用

    目录 1. 概述 2. 详论 2.1. 创建材质 2.2. 着色器 2.2.1. 名称 2.2.2. 属性 2.2.3. SubShader 2.2.3.1. 标签(Tags) 2.2.3.2. 渲染 ...

  4. Unity3D学习笔记4——创建Mesh高级接口

    目录 1. 概述 2. 详论 3. 其他 4. 参考 1. 概述 在文章Unity3D学习笔记2--绘制一个带纹理的面中使用代码的方式创建了一个Mesh,不过这套接口在Unity中被称为简单接口.与其 ...

  5. Unity3D学习笔记6——GPU实例化(1)

    目录 1. 概述 2. 详论 3. 参考 1. 概述 在之前的文章中说到,一种材质对应一次绘制调用的指令.即使是这种情况,两个三维物体使用同一种材质,但它们使用的材质参数不一样,那么最终仍然会造成两次 ...

  6. Unity3D学习笔记8——GPU实例化(3)

    目录 1. 概述 2. 详论 2.1. 自动实例化 2.2. MaterialPropertyBlock 3. 参考 1. 概述 在前两篇文章<Unity3D学习笔记6--GPU实例化(1)&g ...

  7. Unity3D学习笔记12——渲染纹理

    目录 1. 概述 2. 详论 3. 问题 1. 概述 在文章<Unity3D学习笔记11--后处理>中论述了后处理是帧缓存(Framebuffer)技术实现之一:而另外一个帧缓存技术实现就 ...

  8. [Modern OpenGL系列(三)]用OpenGL绘制一个三角形

    本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51347008 在上一篇文章中已经介绍了OpenGL窗口的创建.本文接着说如 ...

  9. unity3d学习笔记(一) 第一人称视角实现和倒计时实现

    unity3d学习笔记(一) 第一人称视角实现和倒计时实现 1. 第一人称视角 (1)让mainCamera和player(视角对象)同步在一起 因为我们的player是生成的,所以不能把mainCa ...

随机推荐

  1. repo sync error: .repo/manifests/: contains uncommitted changes

    andorid 源码库同步,报错如标题,好几次想放弃,尝试了网上不少办法,无效, 最终从 stackoverflow 得到答案: After issued repo sync, I got a err ...

  2. 偷梁换柱:使用mock.patch辅助python单元测试

    最近在搞软工项目的后端测试,重新复习了一下python的mock.patch,并用它简化了对一些复杂逻辑的测试,在此记录 问题描述 本组的项目比较特殊,设计对教务网站的模拟登陆与信息爬取,同时不少接口 ...

  3. Journey to the future begins

    当提交申请的那一刻,我就更加确认了自己想要走的路,慢慢一路向上,追求自己想要的生活! 2021.5.18 wzb

  4. Spring Boot 允许跨域设置失败的问题深究

    在公司开发过程中,一个前后端分离的项目遇见了跨域的问题. 前端控制台报错:No 'Access-Control-Allow-Origin' header is present on the reque ...

  5. WIKI和JIRA-安装与使用

    1.Wiki介绍1.1 Wiki(多人协作的写作系统)是一种超文本系统,这种超文本系统支持面向社群的协作式写作,即人人可编辑.在公司的项目管理中,可以把它当作文档管理和信息组织(Portlet)系统来 ...

  6. mysql3_pymysql

    python数据库编程 1.pyshon数据库接口(python DB-API) 1.为开发人员提供的数据库应用编程接口 2.python支持的数据库服务软件 mysql,oracle,sql_ser ...

  7. shell基础之99乘法表

    方法一: 1 #!/bin/bash 2 for a in {1..9};do 3 for b in {1..9};do 4 c=`echo "$a*$b" |bc` 5 if [ ...

  8. VMWare虚拟机显示模块“Disk”启动失败

    找到启动虚拟机的目录: 在此路径中找到.vmx文件,在文件中查找(Ctrl+F快速查找)vmci0.present,此时会看到"vmci0.present = "TRUE" ...

  9. GPIO模式用法

    浮空,顾名思义就是浮在半空,输入直接与寄存器挂钩: 开漏,输出0的时候 PMOS管导通IO输出Vdd,输出1的时候 NMOS管导通IO输出Vss(Cmos场效应管): 推挽,输出时候电平确定,同样使用 ...

  10. 为何使用thrift-rpc与http的选择

    在工作中偶然看到公司旧架构在loaclserver中使用的是thrift,遂记录一下 thrif作为一种rpc框架 接口描述语言和二进制通信协议,至于为何使用thrift 其问题本质是为何在已有htt ...