【Unity3D】使用GL绘制线段
1 前言
线段渲染器LineRenderer、拖尾TrailRenderer、绘制物体表面三角形网格从不同角度介绍了绘制线段的方法,本文再介绍一种新的绘制线段的方法:使用 GL 绘制线段。
Graphics Library(简称 GL),包含一系列类似 OpenGL 的 Immediate 模式的渲染指令,比 Graphic.DrawMesh() 更高效。GL 是立即执行的,如果在Update() 方法里调用,它们将在相机渲染前执行,相机渲染前会清空屏幕,GL 渲染效果将无法看到。通常 GL 用法是:在相机上挂脚本,并在 OnPostRender() 方法里执行(MonoBehaviour的生命周期)。GL 渲染的图像不需要 GameObject 承载,在 Hierarchy 窗口不会生成 GameObject 对象。
2 代码实现
LinePainter.cs
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class LinePainter : MonoBehaviour {
private Material lineMaterial; // 线段材质
private Vector3[][] circleVertices; // 3个圆环的顶点坐标
private Color[] colors; // 3 个圆环的颜色
private void Start() {
lineMaterial = new Material(Shader.Find("Hidden/Internal-Colored"));
colors = new Color[] {Color.red, Color.green, Color.blue};
circleVertices = new Vector3[3][];
for (int i = 0; i < 3; i++) {
circleVertices[i] = GetCircleLines(Vector3.up, Vector3.one, i, 20);
}
}
private void OnPostRender() { // GL处理不能放在Update里
for (int i = 0; i < 3; i++) {
DrawLines(circleVertices[i], colors[i], Color.yellow, 0.01f);
}
}
private Vector3[] GetCircleLines(Vector3 center, Vector3 radius, int axis, int num) { // 获取圆环顶点数据
Vector3[] vertices = new Vector3[num + 1];
float ds = Mathf.PI * 2.0f / num;
float theta = 0;
if (axis == 0)
{
for (int i = 0; i <= num; i++) {
theta += ds;
Vector3 vec = new Vector3(0, Mathf.Cos(theta), Mathf.Sin(theta));
vertices[i] = new Vector3(vec.x * radius.x, vec.y * radius.y, vec.z * radius.z) + center;
}
} else if (axis == 1) {
for (int i = 0; i <= num; i++) {
theta += ds;
Vector3 vec = new Vector3(Mathf.Cos(theta), 0, Mathf.Sin(theta));
vertices[i] = new Vector3(vec.x * radius.x, vec.y * radius.y, vec.z * radius.z) + center;
}
} else {
for (int i = 0; i <= num; i++) {
theta += ds;
Vector3 vec = new Vector3(Mathf.Cos(theta), Mathf.Sin(theta), 0);
vertices[i] = new Vector3(vec.x * radius.x, vec.y * radius.y, vec.z * radius.z) + center;
}
}
return vertices;
}
private void DrawLines(Vector3[] points, Color lineColor, Color pointColor, float pointSize) { // 绘制线段
GL.PushMatrix();
GL.LoadIdentity();
GL.MultMatrix(GetComponent<Camera>().worldToCameraMatrix);
GL.LoadProjectionMatrix(GetComponent<Camera>().projectionMatrix);
lineMaterial.SetPass(0);
AddLines(points, lineColor);
if (pointColor != null && pointSize > 0) {
AddPoints(points, pointColor, pointSize);
}
GL.PopMatrix();
}
private void AddLines(Vector3[] points, Color color) { // 添加线段端点
GL.Begin(GL.LINE_STRIP);
GL.Color(color);
foreach (Vector3 point in points)
{
GL.Vertex3(point.x, point.y, point.z);
}
GL.End();
}
private void AddPoints(Vector3[] points, Color color, float size) { // 添加绘制点(通过绘制小立方模拟顶点)
GL.Begin(GL.QUADS);
GL.Color(color);
foreach (Vector3 point in points) {
// 前面
GL.Vertex3(point.x + size, point.y + size, point.z - size);
GL.Vertex3(point.x + size, point.y - size, point.z - size);
GL.Vertex3(point.x - size, point.y - size, point.z - size);
GL.Vertex3(point.x - size, point.y + size, point.z - size);
// 后面
GL.Vertex3(point.x + size, point.y + size, point.z + size);
GL.Vertex3(point.x + size, point.y - size, point.z + size);
GL.Vertex3(point.x - size, point.y - size, point.z + size);
GL.Vertex3(point.x - size, point.y + size, point.z + size);
// 上面
GL.Vertex3(point.x + size, point.y + size, point.z + size);
GL.Vertex3(point.x + size, point.y + size, point.z - size);
GL.Vertex3(point.x - size, point.y + size, point.z - size);
GL.Vertex3(point.x - size, point.y + size, point.z + size);
// 下面
GL.Vertex3(point.x + size, point.y - size, point.z + size);
GL.Vertex3(point.x + size, point.y - size, point.z - size);
GL.Vertex3(point.x - size, point.y - size, point.z - size);
GL.Vertex3(point.x - size, point.y - size, point.z + size);
// 左面
GL.Vertex3(point.x - size, point.y + size, point.z + size);
GL.Vertex3(point.x - size, point.y + size, point.z - size);
GL.Vertex3(point.x - size, point.y - size, point.z - size);
GL.Vertex3(point.x - size, point.y - size, point.z + size);
// 右面
GL.Vertex3(point.x + size, point.y + size, point.z + size);
GL.Vertex3(point.x + size, point.y + size, point.z - size);
GL.Vertex3(point.x + size, point.y - size, point.z - size);
GL.Vertex3(point.x + size, point.y - size, point.z + size);
}
GL.End();
}
}
说明: LinePainter 脚本组件需要挂在相机下。
SceneController.cs
using UnityEngine;
public class SceneController : MonoBehaviour {
private Transform cam; // 相机
private float nearPlan; // 近平面
private Vector3 preMousePos; // 上一帧的鼠标坐标
private int keyStatus = 0; // 鼠标样式状态
private bool isDraging = false; // 是否在拖拽中
void Start() {
cam = Camera.main.transform;
nearPlan = Camera.main.nearClipPlane;
}
void Update() {
keyStatus = GetKeyStatus();
UpdateScene(); // 更新场景(Ctrl+Scroll: 缩放场景, Ctrl+Drag: 平移场景, Alt+Drag: 旋转场景)
}
private int GetKeyStatus() { // 获取按键状态(0: 默认, 1: 缩放或平移, 2: 旋转)
if (isDraging) {
return keyStatus;
}
if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.LeftControl)) {
return 1;
}
if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.LeftAlt)) {
return 2;
}
return 0;
}
private void UpdateScene() { // 更新场景(Ctrl+Scroll: 缩放场景, Ctrl+Drag: 平移场景, Alt+Drag: 旋转场景)
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (!isDraging && keyStatus == 1 && Mathf.Abs(scroll) > 0) { // 缩放场景
ScaleScene(scroll);
} else if (Input.GetMouseButtonDown(0)) {
preMousePos = Input.mousePosition;
isDraging = true;
} else if (Input.GetMouseButtonUp(0)) {
isDraging = false;
} else if (Input.GetMouseButton(0)) {
Vector3 offset = Input.mousePosition - preMousePos;
if (keyStatus == 1) { // 移动场景
MoveScene(offset);
} else if (keyStatus == 2) { // 旋转场景
RotateScene(offset);
}
preMousePos = Input.mousePosition;
}
}
private void ScaleScene(float scroll) { // 缩放场景
cam.position += cam.forward * scroll;
}
private void MoveScene(Vector3 offset) { // 平移场景
cam.position -= (cam.right * offset.x / 100 + cam.up * offset.y / 100);
}
private void RotateScene(Vector3 offset) { // 旋转场景
Vector3 rotateCenter = GetRotateCenter(0);
cam.RotateAround(rotateCenter, Vector3.up, offset.x / 3); // 水平拖拽分量
cam.LookAt(rotateCenter);
cam.RotateAround(rotateCenter, -cam.right, offset.y / 5); // 竖直拖拽分量
}
private Vector3 GetRotateCenter(float planeY) { // 获取旋转中心
if (Mathf.Abs(cam.forward.y) < Vector3.kEpsilon || Mathf.Abs(cam.position.y) < Vector3.kEpsilon)
{
return cam.position + cam.forward * (nearPlan + 1 / nearPlan);
}
float t = (planeY - cam.position.y) / cam.forward.y;
float x = cam.position.x + t * cam.forward.x;
float z = cam.position.z + t * cam.forward.z;
return new Vector3(x, planeY, z);
}
}
说明: SceneController 脚本组件用于控制相机位置和姿态,便于从不同角度查看绘制的线段,其原理介绍见→缩放、平移、旋转场景。
3 运行效果

声明:本文转自【Unity3D】使用GL绘制线段
【Unity3D】使用GL绘制线段的更多相关文章
- Unity编辑器 - 使用GL绘制控件
Unity编辑器 - 使用GL绘制控件 控件较为复杂时,可能造成界面卡顿,在EditorGUI中也可以灵活使用GL绘制来提升性能. 以绘制线段为例: using UnityEngine; using ...
- 关于unity3D的GL图像库的使用
GL图象库 GL图象库是底层的图象库,主要功能是使用程序来绘制常见的2D与3D几何图形.这些图形具有一定的特殊性,他们不属于3D网格图形,只会以面的形式渲染.使用GL图象库,可在屏幕中绘制2D几何图形 ...
- Quartz2D复习(一)--- 基础知识 / 绘制线段圆弧 / 图片水印 / 截图
1.Quartz 2D是一个二维绘图引擎,同时支持ios和Mac系统: Quart2D的API是纯C语言的,API来自于Core Graphics框架: 2.Quartz 2D可以绘制图形(线段/三 ...
- C#GDI+ 绘制线段(实线或虚线)、矩形、字符串、圆、椭圆
C#GDI+ 绘制线段(实线或虚线).矩形.字符串.圆.椭圆 绘制基本线条和图形 比较简单,直接看代码. Graphics graphics = e.Graphics; //绘制实线 )) { pen ...
- 初级入门 --- web GL绘制点
" 万丈高楼平地起." 01基础知识 一.相关术语 图元 :WebGL 能够绘制的基本图形元素,包含三种:点.线段.三角形. 片元:可以理解为像素,像素着色阶段是在片元着色器中. ...
- Unity3D中灵活绘制进度条
有时我们需要在Unity3D中绘制进度条,如: 或 如果使用4.6版本以下的unity绘制环形的进度条可能需要费点劲.我搜到的大多数方法都是用NGUI插件,但有时只是 ...
- Android OpenGL ES(九)绘制线段Line Segment .
创建一个DrawLine Activity,定义四个顶点: float vertexArray[] = { -0.8f, -0.4f * 1.732f, 0.0f, -0.4f, 0.4f * 1.7 ...
- gl 绘制多边形的函数解析 分类: OpenGL(转)
http://blog.csdn.net/zhongjling/article/details/7528091 1,所谓正反面 glFrontFace(GL_CCW); // 设置CCW方向为“正面 ...
- unity3d 使用GL 方式画线
这个是画线部分 private Vector3[] linePoints; public int m_LineCount; public int m_PointUsed; public void Re ...
- Unity使用OpenGL绘制线段
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ShowGrid ...
随机推荐
- SD Host控制器的系统集成
控制器集成需求 SD Host可以从外部读取数据存储到SRAM和eFlash或者可以从内部读取数据输出到外部存储 AHB Bus slave接口用于配置 master接口作为主设备,DMA可以控制总线 ...
- [转帖]TiDB-merge region相关问题
一.开启region merge # 控制 Region Merge 的 size 上限,当 Region Size 大于指定值时 PD 不会将其与相邻的 Region 合并 pd-ctl confi ...
- [转帖]Split Region 使用文档
https://docs.pingcap.com/zh/tidb/stable/sql-statement-split-region 在 TiDB 中新建一个表后,默认会单独切分出 1 个 Regio ...
- minio性能测试
minio性能测试 minio的使用 前期使用了s3fs 但是想验证一下性能相关, 所以使用今天简单验证了一下, 其实也可以使用一下fio 但是s3fs 是对象存储 没有修改 只有上传, 所以感觉还是 ...
- [转帖]tidb 修改root密码
http://blog.51yip.com/tidb/2452.html 通过 {pd-ip}:{pd-port}/dashboard 登录 TiDB Dashboard,登录用户和口令为 TiD ...
- [转帖]A17再次证明苹果才是王者,组装芯片的安卓手机给它提鞋都不配
http://news.sohu.com/a/653472711_121124371 在挤了两代牙膏之后,苹果终于拿出了性能大幅提升的A17处理器,外媒传出A17处理器的性能提升幅度至少超过四成,相比 ...
- [转帖]宁可信鬼,也不信 iowait 这张嘴!
https://zhuanlan.zhihu.com/p/407333624 原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处. 我们经常遇到iowait这个名词,在top命 ...
- awk的简单样例
shell awk求和 当第一列相同时,对应的第二列相加 awk'{sum[$1]+=$2}END{for(c in sum){print c,sum[c]}}'输入文件名 在Shell中,我们可以用 ...
- [粘贴]环绕闸极不能让三星在3nm工艺领先台积电
环绕闸极不能让三星在3nm工艺领先台积电 http://www.pinlue.com/article/2019/08/1510/599507978757.html 转身遇见她 2019-08-15 收 ...
- c++基础之语句
上一次总结了一下c++中表达式的相关内容,这篇博文主要总结语句的基础内容 简单语句 c++ 中语句主要是以分号作为结束符的,最简单的语句是一个空语句,空语句主要用于,语法上需要某个地方,但是逻辑上不需 ...