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绘制线段的更多相关文章

  1. Unity编辑器 - 使用GL绘制控件

    Unity编辑器 - 使用GL绘制控件 控件较为复杂时,可能造成界面卡顿,在EditorGUI中也可以灵活使用GL绘制来提升性能. 以绘制线段为例: using UnityEngine; using ...

  2. 关于unity3D的GL图像库的使用

    GL图象库 GL图象库是底层的图象库,主要功能是使用程序来绘制常见的2D与3D几何图形.这些图形具有一定的特殊性,他们不属于3D网格图形,只会以面的形式渲染.使用GL图象库,可在屏幕中绘制2D几何图形 ...

  3. Quartz2D复习(一)--- 基础知识 / 绘制线段圆弧 / 图片水印 / 截图

    1.Quartz 2D是一个二维绘图引擎,同时支持ios和Mac系统: Quart2D的API是纯C语言的,API来自于Core  Graphics框架: 2.Quartz 2D可以绘制图形(线段/三 ...

  4. C#GDI+ 绘制线段(实线或虚线)、矩形、字符串、圆、椭圆

    C#GDI+ 绘制线段(实线或虚线).矩形.字符串.圆.椭圆 绘制基本线条和图形 比较简单,直接看代码. Graphics graphics = e.Graphics; //绘制实线 )) { pen ...

  5. 初级入门 --- web GL绘制点

    " 万丈高楼平地起." 01基础知识 一.相关术语 图元 :WebGL 能够绘制的基本图形元素,包含三种:点.线段.三角形. 片元:可以理解为像素,像素着色阶段是在片元着色器中. ...

  6. Unity3D中灵活绘制进度条

    有时我们需要在Unity3D中绘制进度条,如:           或        如果使用4.6版本以下的unity绘制环形的进度条可能需要费点劲.我搜到的大多数方法都是用NGUI插件,但有时只是 ...

  7. Android OpenGL ES(九)绘制线段Line Segment .

    创建一个DrawLine Activity,定义四个顶点: float vertexArray[] = { -0.8f, -0.4f * 1.732f, 0.0f, -0.4f, 0.4f * 1.7 ...

  8. gl 绘制多边形的函数解析 分类: OpenGL(转)

    http://blog.csdn.net/zhongjling/article/details/7528091 1,所谓正反面 glFrontFace(GL_CCW);  // 设置CCW方向为“正面 ...

  9. unity3d 使用GL 方式画线

    这个是画线部分 private Vector3[] linePoints; public int m_LineCount; public int m_PointUsed; public void Re ...

  10. Unity使用OpenGL绘制线段

    using System.Collections; using System.Collections.Generic; using UnityEngine; public class ShowGrid ...

随机推荐

  1. jenkins构建报错: Send build artifacts over SSH' changed build result to UNSTABLE

    原因包括: ssh配置的用户没有相关的权限. 最好是配置root用户

  2. [转帖]CTF -bugku-misc(持续更新直到全部刷完)

    CTF -bugku-misc(持续更新直到全部刷完) https://www.cnblogs.com/cat47/p/11432475.html 1.签到题 点开可见.(这题就不浪费键盘了) CTF ...

  3. [转帖]如何升级vCenter(VCSA)补丁?

    https://www.dinghui.org/upgrade-vcsa-patch.html VCSA的升级分为repo源和iso镜像两种方式,由于使用repo源进行升级只支持HTTPS和FTPS协 ...

  4. 【转帖】基于paramiko的二次封装

    https://www.jianshu.com/p/944674f44b24 paramiko 是 Python 中的一个用来连接远程主机的第三方工具,通过使用 paramiko 可以用来代替以 ss ...

  5. [转帖]使用JAYDEBEAPI同时连接两个不同数据库(ORACLE+MYSQL)的问题

    jaydebeapi 同时连接两种数据库 在使用jaydebeapi只连接一种数据库时,是没问题的,但是如果需要同时连接两种数据库,比如同时连接oracle和mysql 例如以下测试代码:   imp ...

  6. [转帖]疑问:进程在竞争CPU时并没有真正运行,为什么还会导致系统的负载升高?

    疑问:进程在竞争CPU时并没有真正运行,为什么还会导致系统的负载升高? 因为存在CPU上下文切换. linux系统说明 Linux是一个多任务操作系统,它支持远大于CPU数量的任务同时运行.当然,这些 ...

  7. 【译文】IEEE白皮书 6G 太赫兹技术的基本原理 2023版

    第一章 简介 太赫兹波是介于微波和光波之间的光谱区域,频率从 0.1THz ~ 10THz 之间,波长在 3mm ~ 30μm 之间.提供大块连续的频带范围以满足对 Tbit/s 内极高数据传输速率的 ...

  8. SpringBoot中Bean的条件装配

    目录 概述 Profile Conditional ConditionalOn ConditionalOnProperty 概述 众所周知,SpringBoot最腻害的地方就是容器,开发人员的日常工作 ...

  9. windwos使用FRP方式

    FRP使用方法 流程图如下 建议查看流程图哦 访问FRP官方项目 https://freefrp.net 下面是windwos演示 进入网站选择客户端下载 客户端版本选择 windwos是adm64 ...

  10. 把Unity的日志保存到文件中

    Unity的日志事件 Unity提供了两个日志回调API,这两个回调函数的参数都是一样的,通过这个API可以在真机上把Debug.Log/LogWarning/LogError 日志输出到文件中保存, ...