|
梅川依福:Unity Graphics API 深入浅出-GL(1)
前言
前面一节对GL做了一个基础知识的了解,知道了Unity的GL库可以用来绘制一些基础的图形彵和OpenGL的接口设计上基本相同。本节主要以测试代码为主,进一步了解GL基础图形的绘制和UV映射,
1、主要知识点
1、Begin中的mode有哪些
2、顶点颜色设置
3、缠绕顺序
4、共用顶点,
5、unity3d左手坐标系
6、了解UV映射。
2、GL.Begin的mode参数
与OpenGL与UNITY3D的对比
OPENGL | UNITY3D | 解释 | GL_LINE_STRIP | LINE_STRIP | 绘制线条带 | GL_LINES | LINES | 绘制线条,每两点构成一条线段 | GL_QUADS | QUADS | 绘制四边形 | GL_TRIANGLES_STRIP | TRIANGLE_STRIP | 绘制三角形带 | GL_TRIANGLES | TRIANGLES | 绘制三角形 | GL_POINTS | 无 | 一系列独立的顶点 | GL_POLYGON | 无 | 简单、凸多边形的边界 | GL_LINE_LOOP | 无 | 顶点相连,连成一系列线段,头尾连 | GL_TRINGLE_FAN | 无 | 相连的三角形扇形 | GL_QUAD_STRIP | 无 | 相连的四边形 |
GL.Begin的效果
https://www.zhihu.com/video/1568363979868413952
一、使用GL画图
1、LINE_STRIP
目标:了解顶点颜色设置
public int circleCount = 6;
//使用LINE_STRIP
private void DrawCircle()
{
float angleDelta = 2 * Mathf.PI / circleCount;
GL.Begin(GL.LINE_STRIP);
for (int i = 0; i < circleCount + 1; i++)
{
float angle = angleDelta * i;
float angleNext = angle + angleDelta;
if (i % 2 == 0)
{
GL.Color(Color.red);
}
else
{
GL.Color(Color.green);
}
GL.Vertex3(Mathf.Cos(angle) * circleRadius, Mathf.Sin(angle) * circleRadius, 0);
}
GL.End();
}
运行结果
以上是使用GL.Begin(GL.LINE_STRIP)通过绘制连续的点显示出的图像效果,通过代码可以看到每条线的起始点颜色都为红色,结束点为绿色,在渲染过程中可以看到顶点的颜色是逐渐过渡的,这是因为在渲染期间顶点与顶点间的颜色值会做插值的结果,线段的颜色插值并不是太明显,我们可以通过GL.TRIANGLES进一步了解颜色插值。
2、GL.TRIANGLES
目标:缠绕顺
public int triangleSize = 2;
//使用TRIANGLES进行绘制三角形
private void DrawTriangle()
{
GL.Begin(GL.TRIANGLES);
//顺时针
GL.Color(Color.red);
GL.Vertex3(-triangleSize, -triangleSize, 0);//左下
GL.Color(Color.green);
GL.Vertex3(0, triangleSize, 0);//上
GL.Color(Color.blue);
GL.Vertex3(triangleSize, -triangleSize, 0);//右下
GL.End();
}
为了让三角形看起来更有意思我给每个顶点都加上了颜色,左下角红色,正上为绿色,右下角为蓝色的顺序进行顶点设置,你是否观察到顶点位置赋值是有顺序的,顺序有什么用处理呢?
缠绕顺序:缠绕顺序有正和逆之分,顺时针的顶点缠绕顺序表示为物体的正面,从另一面看就是逆时针我们约定为物体的背面。
有了正面和背面之分,我们就可以在Shader中通过打开Cull Back设置背面剔除减少需要渲染的片元,这样可以有效的提升渲染效率。
当然材质如果使用了Cull off 的话是可以把背面剔除关闭,GL提供了GL.invertCulling可以设置是否打开背面剔除,但是GL.invertCulling是否生效主要还是看材质本身的设置,GL.invertCulling面对“Hidden/Internal-Colored”Shader就显得无力。
3、GL.TRIANGLE_STRIP
目标:共用顶点
private void DrawTriangles()
{
GL.Begin(GL.TRIANGLE_STRIP);
GL.Color(Color.red);
GL.Vertex3(-triangleSize, -triangleSize, 0);
GL.Color(Color.green);
GL.Vertex3(0, triangleSize, 0);
GL.Color(Color.blue);
GL.Vertex3(triangleSize, -triangleSize, 0);
GL.Color(Color.yellow);
GL.Vertex3(triangleSize, triangleSize, 0);
GL.End();
}
四个顶点绘制两个三角形
可以通过上图看出我们给出了四个点却画出了两个三角形(理论上需要6个点才可以画两个三角形),其中123组成一个三角形,234组成了一个三角形,在这里234共用了23两个顶点,如果继续绘制下去被共用的总是最后两个顶点,这样可以有效的节省顶点信息,关于共用顶点在OpenGL中的网络优化上是一个重点要处理的过程,在Unity中默认进行了网络优化。
4、GL.QUADS
目标:Unity左手坐标系
绘制CUBE的代码
private void DrawCube()
{
GL.Begin(GL.QUADS);
//正面
GL.Color(Color.red);
GL.Vertex3(-quadSize, -quadSize, -quadSize);
GL.Vertex3(quadSize, -quadSize, -quadSize);
GL.Vertex3(quadSize, quadSize, -quadSize);
GL.Vertex3(-quadSize, quadSize, -quadSize);
GL.Color(Color.green);
GL.Vertex3(-quadSize, -quadSize, quadSize);
GL.Vertex3(quadSize, -quadSize, quadSize);
GL.Vertex3(quadSize, quadSize, quadSize);
GL.Vertex3(-quadSize, quadSize, quadSize);
GL.Color(Color.blue);
GL.Vertex3(quadSize, -quadSize, -quadSize);
GL.Vertex3(quadSize, -quadSize, quadSize);
GL.Vertex3(quadSize, quadSize, quadSize);
GL.Vertex3(quadSize, quadSize, -quadSize);
GL.Color(Color.yellow);
GL.Vertex3(-quadSize, -quadSize, -quadSize);
GL.Vertex3(-quadSize, -quadSize, quadSize);
GL.Vertex3(-quadSize, quadSize, quadSize);
GL.Vertex3(-quadSize, quadSize, -quadSize);
//底面
GL.Color(Color.gray);
GL.Vertex3(-quadSize, -quadSize, -quadSize);
GL.Vertex3(-quadSize, -quadSize, quadSize);
GL.Vertex3(quadSize, -quadSize, quadSize);
GL.Vertex3(quadSize, -quadSize, -quadSize);
//顶面
GL.Color(Color.black);
GL.Vertex3(-quadSize, quadSize, -quadSize);
GL.Vertex3(quadSize, quadSize, -quadSize);
GL.Vertex3(quadSize, quadSize, quadSize);
GL.Vertex3(-quadSize, quadSize, quadSize);
GL.End();
}
运行效果
CUBE
https://www.zhihu.com/video/1568371860877840384
画立体正方形的时候我们需要先了解一下左手与右手坐标系
左右手坐标系
如何识记这三个轴的颜色和对应关系(RGB法),R: 大姆指X G:食指Y B:中指Z
x轴:大姆指的方向(红色)
y轴:食指的方向(绿色)
z轴:中指的方向 (蓝色) 指向屏幕里的是左手坐标系,指向屏幕外的是右手坐标系
左右手法则的区别只是Z的朝向。伸出你的左右手,在Unity的世界坐标系中是以左手坐标系为主,下一个节会对坐标系再进行一次升化的讲解。
二、简单UV映射
完成了Unity的一些GL的顶点操作,知道设置顶点指定颜色可以让线和面有过渡色,虽然过渡色看起来不坏,但是我们更希望给对象加上一些图片(纹理),可是要如何给这些面加上图片呢?
1、从如何敷面膜开始
我们先来思考一下,给你一张适合脸部大小的面膜,让他把面膜贴在脸上,要如何操作呢?面膜上有多个区域如鼻子区域,眼睛区域,我们希望把相应的区域贴合在我们面部对应的区域。我们在敷的时候总希望鼻尖上的点可以和面膜鼻头区域鼻尖上的点重合在一起这样可以保证完美的贴合,每个脸部的位置最好成面膜上有对应的点一一对应,这样就可以更好的让面膜覆盖到我们的整个脸部。
面膜和UV
我们进一步思考,如果给一个正方形面片上贴上图片,其实和贴面膜是一样的,我们的脸好比是正方形面片(当然我们的脸不是正方形的),而面膜好比是图片。我们最好的办法是给正方形面片的的四个顶点都打上胶水,然后让图片的四个角贴上去。那在Unity3D中我们要如何操作呢,而打上“胶水”要怎么打呢?如何打胶就是我们说的UV映射
2、什么是UV映射
uv映射就是将二维的贴图映射到对象的一个面(或者多个面)上,通俗的说给Mesh上所有的点找到纹理对应显示的位置。我们把纹理的横坐标叫U,把纹理的纵坐标叫V,其实就是图片的XY,只是换了一个说法让人一看就明白(好高深,纸老虎)。
UV通常是0-1之间,这样的好处就是图片的放大缩小不会影响实际UV映射后的显示,如果打开mipmap多级纹理的话可以让计算机在不同远近时选择不同纹理大小的显示。当然UV也可能是大于1的多象限UV,如果想了解高清UV纹理的映射可以查找UDIM,主要是解决高清对象显示因为float值精度不能准确索引像素的问题,有机会慢慢道来。
举个例子:
下图左边有一个Cube 边长为10,需要在Cube中间位置(5,5,0)贴上“二哈”的鼻子的图片,刚好鼻子位置在图片的(0.5,0.5)的位置,那这个位置的UV值为(0.5,0.5),如果这个图片使用最右边等比例缩放图时也是在(0.5,0.5)上那UV映射只要是等比较的图片都能很好的被定位到。看来UV是一个好东西。
3、代码分析
准备好“胶水”,TexCoord可以设置顶点对应的纹理UV
给顶点设置纹理坐标
public Material _materialPic;//画图所使用的Pic
public float _PicSize = 5.0f;
private void DrawPic()
{
if(_materialPic == null)
{
return;
}
_materialPic.SetPass(0);
GL.PushMatrix();
GL.Begin(GL.QUADS);
GL.Color(Color.white);
GL.TexCoord2(0, 0);
GL.Vertex3(-_PicSize, -_PicSize, 0);
GL.TexCoord2(0, 1);
GL.Vertex3(-_PicSize, _PicSize, 0);
GL.TexCoord2(1, 1);
GL.Vertex3(_PicSize, _PicSize, 0);
GL.TexCoord2(1, 0);
GL.Vertex3(_PicSize, -_PicSize, 0);
GL.End();
GL.PopMatrix();
}
准备一张“面膜”(材质),品牌选择:创建一个Material使用Shader为Unlit/Texture, 所使用的精华液(纹理)为“二哈”神奇药水。
“二哈”精华液,是一个2D图片纹理类型选择Sprtie(2D and UI)
2D“哈士奇”精华液
目标Mesh,使用的是QUADS四个顶点组成的Mesh,所使用的坐标系为以中心点为中心的坐标系
四个顶点所标识在空间中的位置
作用纹理 :&#34;二哈&#34;纹理的坐标系和Mesh的坐标系不一样,不同的引擎纹理坐标系也不相同,在Unity中是左下角坐标系
纹理映射
从字面理解UV映射有点难明白,如果我们说给Mesh贴上纹理那就比较容易理解了,当然本节的UV映射相对比较简单。
三、全部代码
using UnityEngine;
public class Graphics02Shape : MonoBehaviour
{
public enum DrawingType
{
DRAW_CIRCLE,
DRAW_TRIANGLE,
DRAW_TRINAGLES,
DRAW_CUBE,
DRAW_PIC
}
public DrawingType type = DrawingType.DRAW_CIRCLE;
public int circleRadius = 3;
public bool _bRotate = false;
private Material _shapeMaterial;
private void SetMaterialPass()
{
if (_shapeMaterial == null)
{
_shapeMaterial = new Material(Shader.Find(&#34;Hidden/Internal-Colored&#34;));
}
_shapeMaterial.SetPass(0);
}
private void Update()
{
if(_bRotate)
{
transform.RotateAround(transform.position, Vector3.up, 0.6f);
}
}
public int circleCount = 6;
//使用LINE_STRIP
private void DrawCircle()
{
float angleDelta = 2 * Mathf.PI / circleCount;
GL.Begin(GL.LINE_STRIP);
for (int i = 0; i < circleCount + 1; i++)
{
float angle = angleDelta * i;
float angleNext = angle + angleDelta;
if (i % 2 == 0)
{
GL.Color(Color.red);
}
else
{
GL.Color(Color.green);
}
GL.Vertex3(Mathf.Cos(angle) * circleRadius, Mathf.Sin(angle) * circleRadius, 0);
}
GL.End();
}
public int triangleSize = 2;
//使用TRIANGLES进行绘制三角形
private void DrawTriangle()
{
GL.Begin(GL.TRIANGLES);
//顺时针
GL.Color(Color.red);
GL.Vertex3(-triangleSize, -triangleSize, 0);//左下
GL.Color(Color.green);
GL.Vertex3(0, triangleSize, 0);//上
GL.Color(Color.blue);
GL.Vertex3(triangleSize, -triangleSize, 0);//右下
GL.End();
}
private void DrawTriangles()
{
GL.Begin(GL.TRIANGLE_STRIP);
GL.Color(Color.red);
GL.Vertex3(-triangleSize, -triangleSize, 0);
GL.Color(Color.green);
GL.Vertex3(0, triangleSize, 0);
GL.Color(Color.blue);
GL.Vertex3(triangleSize, -triangleSize, 0);
GL.Color(Color.yellow);
GL.Vertex3(triangleSize, triangleSize, 0);
/*GL.Color(Color.gray);
GL.Vertex3(2*triangleSize, triangleSize, 0);*/
GL.End();
}
public int quadSize = 2;
//使用QUADS绘制四边形
private void DrawQuad()
{
GL.Begin(GL.QUADS);
GL.Color(Color.blue);
GL.Vertex3(-quadSize, -quadSize, 0);//左下
GL.Vertex3(-quadSize, quadSize, 0);//左上
GL.Vertex3(quadSize, quadSize, 0);//右上
GL.Vertex3(quadSize, -quadSize, 0);//右下
GL.End();
}
//画立方体
private void DrawCube()
{
GL.Begin(GL.QUADS);
//正面
GL.Color(Color.red);
GL.Vertex3(-quadSize, -quadSize, -quadSize);
GL.Vertex3(quadSize, -quadSize, -quadSize);
GL.Vertex3(quadSize, quadSize, -quadSize);
GL.Vertex3(-quadSize, quadSize, -quadSize);
GL.Color(Color.green);
GL.Vertex3(-quadSize, -quadSize, quadSize);
GL.Vertex3(quadSize, -quadSize, quadSize);
GL.Vertex3(quadSize, quadSize, quadSize);
GL.Vertex3(-quadSize, quadSize, quadSize);
GL.Color(Color.blue);
GL.Vertex3(quadSize, -quadSize, -quadSize);
GL.Vertex3(quadSize, -quadSize, quadSize);
GL.Vertex3(quadSize, quadSize, quadSize);
GL.Vertex3(quadSize, quadSize, -quadSize);
GL.Color(Color.yellow);
GL.Vertex3(-quadSize, -quadSize, -quadSize);
GL.Vertex3(-quadSize, -quadSize, quadSize);
GL.Vertex3(-quadSize, quadSize, quadSize);
GL.Vertex3(-quadSize, quadSize, -quadSize);
//底面
GL.Color(Color.gray);
GL.Vertex3(-quadSize, -quadSize, -quadSize);
GL.Vertex3(-quadSize, -quadSize, quadSize);
GL.Vertex3(quadSize, -quadSize, quadSize);
GL.Vertex3(quadSize, -quadSize, -quadSize);
//顶面
GL.Color(Color.black);
GL.Vertex3(-quadSize, quadSize, -quadSize);
GL.Vertex3(quadSize, quadSize, -quadSize);
GL.Vertex3(quadSize, quadSize, quadSize);
GL.Vertex3(-quadSize, quadSize, quadSize);
GL.End();
}
public Material _materialPic;//画图所使用的Pic
public float _PicSize = 5.0f;
private void DrawPic()
{
if(_materialPic == null)
{
return;
}
_materialPic.SetPass(0);
GL.PushMatrix();
GL.Begin(GL.QUADS);
GL.Color(Color.white);
GL.TexCoord2(0, 0);
GL.Vertex3(-_PicSize, -_PicSize, 0);
GL.TexCoord2(0, 1);
GL.Vertex3(-_PicSize, _PicSize, 0);
GL.TexCoord2(1, 1);
GL.Vertex3(_PicSize, _PicSize, 0);
GL.TexCoord2(1, 0);
GL.Vertex3(_PicSize, -_PicSize, 0);
GL.End();
GL.PopMatrix();
}
private void OnRenderObject()
{
SetMaterialPass();
GL.PushMatrix();
GL.MultMatrix(transform.localToWorldMatrix);
switch (type)
{
case DrawingType.DRAW_CIRCLE:
DrawCircle();
break;
case DrawingType.DRAW_TRIANGLE:
DrawTriangle();
break;
case DrawingType.DRAW_TRINAGLES:
DrawTriangles();
break;
case DrawingType.DRAW_CUBE:
DrawCube();
break;
case DrawingType.DRAW_PIC:
DrawPic();
break;
}
GL.PopMatrix();
}
}
四、总结
本节通过GL.Begin的mode学习了顶点颜色的设置函数GL.Color,同时通过GL.Vertex3学习了顺时针的缠绕为正面,还知道了使用四个顶点就可以画出两个三角形的方法,顺便了解了一下左手坐标系。当然最重要的是学习UV映射,明白了如何给Mesh贴上纹理,下一节我们把坐标系进行一个总结。真没想到一个GL我居然两节都没有写完显得有点哆嗦了,但是很值得我们需要从最基础的3D知识学习。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|