【Unity3D】使用GL绘制线段
1.【Unity3D】Unity3D技术栈2.【Unity3D】常用快捷键3.【Unity3D】地形Terrain4.【Unity3D】MonoBehaviour的生命周期5.【Unity3D】Transform组件6.【Unity3D】刚体组件Rigidbody7.【Unity3D】人机交互Input8.【Unity3D】碰撞体组件Collider9.【Unity3D】发射(Raycast)物理射线(Ray)10.【Unity3D】GUI控件11.【Unity3D】相机跟随12.【Unity3D】UGUI概述13.【Unity3D】UGUI之Text14.【Unity3D】UGUI之Button15.【Unity3D】UGUI之Image和RawImage16.【Unity3D】UGUI之Dropdown17.【Unity3D】UGUI之Toggle18.【Unity3D】UGUI之Slider19.【Unity3D】UGUI之布局组件20.【Unity3D】UGUI之InputField21.【Unity3D】摇杆22.【Unity3D】UGUI回调函数23.【Unity3D】血条(HP)24.【Unity3D】角色控制器(CharacterController)25.【Unity3D】Tank大战26.【Unity3D】相机27.【Unity3D】场景切换、全屏_恢复切换、退出游戏、截屏28.【Unity3D】2D动画29.【Unity3D】人体模型及动画30.【Unity3D】人物跟随鼠标位置31.【Unity3D】AudioSource组件32.【Unity3D】动画回调函数、动画事件、动画曲线33.【Unity3D】动画混合34.【Unity3D】IK动画35.【Unity3D】灯光组件Light36.【Unity3D】导航系统37.【Unity3D】动态路障导航38.【Unity3D】分离路面导航39.【Unity3D】拖尾TrailRenderer40.【Unity3D】线段渲染器LineRenderer41.【Unity3D】粒子系统ParticleSystem42.【Unity3D】基于粒子系统实现烟花特效43.【Unity3D】VideoPlayer组件44.【Unity3D】协同程序45.【Unity3D】同步Socket通讯46.【Unity3D】异步Socket通讯47.【Unity3D】Photon环境搭建48.【Unity3D】缩放、平移、旋转场景49.【Unity3D】绘制物体表面三角形网格
50.【Unity3D】使用GL绘制线段
51.【Unity3D】点选物体、框选物体、绘制外边框52.【Unity3D】绘制物体外框线条盒子53.【Unity3D】基于AssetBundle实现资源热更新54.【Unity3D】魔方55.【Unity3D】立方体纹理(Cubemap)和天空盒子(Skybox)56.【Unity3D】Cesium加载大地图57.【Unity3D】UI Toolkit简介58.【Unity3D】UI Toolkit容器59.【Unity3D】UI Toolkit元素60.【Unity3D】UI Toolkit样式选择器61.【Unity3D】UI Toolkit自定义元素62.【Unity3D】UI Toolkit数据动态绑定63.【Unity3D】资源管理64.【Unity3D】Unity与Android交互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绘制线段
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 我与微信审核的“相爱相杀”看个人小程序副业