目录

  • 1.初始化
    • 1.1 设置几何体
    • 1.2 设置着色器
    • 1.3 设置纹理
    • 1.4 设置帧缓存
  • 2.渲染
    • 2.1 渲染到屏幕
    • 2.2 渲染到内存
  • 3.体绘制算法
    • 3.1着色器程序
    • 3.2 进阶
  • 4.代码链接

1.初始化

使用OpenGL绘制图像一般分为两步:初始化阶段和渲染阶段
在初始化阶段对要用到的资源交给OpenGL,在渲染阶段告诉OpenGL如何去使用它们。

1.1 设置几何体

几何体是由顶点构成的,将顶点数据交给OpenGL的方式有很多种,本文使用一种常见的方式。及VAO(EBO, VBO)的方式。
EBO ( Element Buffer Object ) 里面存的是顶点的绘制顺序,如果想绘制三角形,就需要将构成三角形的三个顶点放在一起。(注意:逆时针存放为正面,顺时针为反面)
VBO ( Vertex Buffer Object ) 里面存的是顶点的信息,最基本的就是位置信息Position了。也可以存顶点的颜色、方向方向、纹理坐标等任意需要的信息。可以使用一个VBO存放一种顶点信息 ( Attribute )
VAO ( Vertex Array Buffer Object ) 由一个 EBO 和 数个 VBO 构成,即顶点的绘制顺序和顶点的各种属性。可以理解为一个 VAO 就代表了一个几何体

注意:VAO、VBO、EBO的搭配方式和使用方式是多种多样的,这里使用一种便于理解的方式进行说明。

这里使用OpenGL 4.5进行代码演示

//顶点数据
const float vertices[] = {-1.0,-1.0,0.0,1.0,-1.0,0.0,1.0,1.0,0.0,-1.0,1.0,0.0,
};const UInt indices[] = {0,1,2,0,2,3
};
//创建 VBO EBO VAO
glCreateBuffers(1, &m_iVBO);
glNamedBufferData(m_iVBO, sizeof(vertices), vertices, GL_STATIC_DRAW);glCreateBuffers(1, &m_iEBO);
glNamedBufferData(m_iEBO, sizeof(indices), indices, GL_STATIC_DRAW);glCreateVertexArrays(1, &m_iVAO);//设置VAO的EBO
glVertexArrayElementBuffer(m_iVAO, m_iEBO);//将VBO绑定到VAO的第一个绑定点(vaoBindingIndex )上
UInt vaoBindingIndex = 0;
glVertexArrayVertexBuffer(m_iVAO, vaoBindingIndex, m_iVBO, 0, 3 * sizeof(float));//将VAO的绑定点0上的VBO设置为VAO的第一个属性
UInt vaoAttributeSlot = 0;
glVertexArrayAttribBinding(m_iVAO, vaoAttributeSlot, vaoBindingIndex);
//设置属性的格式(每个顶点Position 为3个float变量)
glVertexArrayAttribFormat(m_iVAO, vaoAttributeSlot, 3, GL_FLOAT, GL_FALSE, 0);
//启用该顶点属性
glEnableVertexArrayAttrib(m_iVAO, vaoAttributeSlot);

1.2 设置着色器

着色器是GPU运行的小程序。顶点着色器用于处理每一个顶点,片元着色器用于处理每一个片元 ( fragment )。片元是光栅化的结果。可能会有多个片元落在同一个像素上。

着色器的设置有什么特别的,可以参考 LearnOpenGL-着色器.,重要的是如何编写体绘制的着色器,这部分在后面进行介绍。

1.3 设置纹理

体绘制处理的体数据是三维的,可以使用一张三维纹理存放它。

//创建三维纹理
glCreateTextures(GL_TEXTURE_3D, 1, &m_iVolume);
//分配三维纹理的内存空间
glTextureStorage3D(m_iVolume, 1, GL_R32F, imageSize[0], imageSize[1], imageSize[2]);
//设置三维纹理的数据
glTextureSubImage3D(m_iVolume,0,0, 0, 0,imageSize[0], imageSize[1], imageSize[2],GL_RED, GL_FLOAT,imageData);

需要注意的是 glTextureStorage3D 的参数GL_R32F是内部格式,即要求OpenGL以怎样的方式使用这张纹理。而 glTextureSubImage3D 中的 GL_RED 和 GL_FLOAT,是外部格式,描述的是传给OpenGL的数据的格式,也可以说是让OpenGL如何解释传给它的imageData。如果内部格式和外部格式不同,OpenGL会进行转化,对于体数据来说,这可能会很费时

1.4 设置帧缓存

如果需要将图像绘制到某个指定的内存中,而不是屏幕上,则需要创建自己的帧缓存,并在绘制之前绑定它。
FBO ( Frame Buffer Object ) 并不直接存放数据,画面中的数据是附件的形式存在的。一个帧缓存可以包含多个附件,因为一帧的内容可能不仅包括我们肉眼看到的图像,如果开启深度测试,帧缓存中还会存放片元在相机中的深度,用来进行深度比较。
因此创建帧缓存时,除了创建 FBO,还需要创建真实存放数据的对象,它可以是一个纹理,也可以是渲染缓存对象 RBO ( Render Buffer Object ) 。
RBO ( Render Buffer Object ) 可以和 FBO 的附件进行绑定

这里使用OpenGL 4.5进行代码演示

m_iTargetSize[0] = 512;
m_iTargetSize[1] = 512;//render to texture
//glCreateTextures(GL_TEXTURE_2D, 1, &m_iRenderTarget);
//glTextureStorage2D(m_iRenderTarget, 1, GL_R32F, m_iTargetSize[0], m_iTargetSize[1]);//render to buffer
glCreateRenderbuffers(2, m_iRBOs);
glNamedRenderbufferStorage(m_iRBOs[0], GL_R32F, m_iTargetSize[0], m_iTargetSize[1]);
glNamedRenderbufferStorage(m_iRBOs[1], GL_DEPTH_COMPONENT, m_iTargetSize[0], m_iTargetSize[1]);
glCreateFramebuffers(1, &m_iFBO);
glNamedFramebufferRenderbuffer(m_iFBO, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_iRBOs[0]);
glNamedFramebufferRenderbuffer(m_iFBO, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_iRBOs[1]);

用纹理做帧缓存附件,可以将一个Pass ( 渲染流程 )的结果传给下一个Pass使用。用RBO做附件,速度更快,一般用来读取渲染结果到内存。

2.渲染

渲染部分就是直接调用函数了。

2.1 渲染到屏幕

void VRT::Render()
{glClearColor(0.0, 0.0, 0.0, 0.0);glClear(GL_COLOR_BUFFER_BIT);glUseProgram(m_iShader);glBindTextureUnit(0, m_iVolume);glBindVertexArray(m_iVAO);glDrawElements(GL_TRIANGLES, sizeof(indices), GL_UNSIGNED_INT, 0);
}

2.2 渲染到内存

void VRT::RenderToTarget(float* _pData, UInt _x, UInt _y)
{if (_x > m_iTargetSize[0] && _y > m_iTargetSize[1]){_pData = nullptr;return;}glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_iFBO);Render();glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);glBindFramebuffer(GL_READ_FRAMEBUFFER, m_iFBO);glReadBuffer(GL_COLOR_ATTACHMENT0);glReadPixels(0, 0, m_iTargetSize[0], m_iTargetSize[1], GL_RED, GL_FLOAT, _pData);glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}

3.体绘制算法

这里使用一个极简的着色器来展示体绘制原理,这个程序只能从一个固定的方向观察体数据。

3.1着色器程序

我们需要绘制一个铺满平面的四边形,顶点着色器里只是将输入的顶点直接输出。四边形的顶点范围是-1到1,直接需要转换到0到1变成纹理坐标,就是每个点在体数据中对应的位置。

#version 460 core
layout(location = 0) in vec3 aPos;
out vec2 texCoord;
void main()
{texCoord = vec2(aPos.x + 1.0, aPos.y + 1.0) * 0.5;gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}

经过OpenGL图元装配和光栅化之后,可以得到一个铺满平面的四边形,这样屏幕的四个角刚好对应四边形的四个角,而屏幕正好对应这体数据的一个面。以这个面上的点作为起始点进行光线步进 ( Ray Marching )

#version 460 core
#define RAY_STEP 10
uniform sampler3D volume;
in vec2 texCoord;
out vec4 FragColor;                                  void main()
{vec3 StartPos = vec3(texCoord.xy, 0.0);//优化点 1vec3 Step = vec3(0.0, 0.0, 0.1); vec3 CurrentPos = StartPos; float res = 0.0;for (int i = 0; i < 10; i++){float value = texture(volume, CurrentPos).r;//优化点 2res += value;//优化点 3CurrentPos += Step;}FragColor = vec4(res);
}

StartPos 是光线步进的起始点,Step是光线步进的步长。
沿着z方向步进十次,每步进一次,在当前位置采样一次体数据,将这十次采样的结果叠加,显示在屏幕上,这便是一个最简单的体绘制了。

3.2 进阶

这个体绘制Demo在以下两个方面进行改进

  1. 光线步进的起始点以及方向
    从不同的方向观察体数据

  2. 使用 Transfer Function 对采样得到的值进行映射
    可以突出显示感兴趣区域,剔除无关数据

  3. 采样结果的叠加方式
    直接影响体绘制效果,稍作修改直接变成Surface Shaded Display, Min/Max Intensity Projection

4.代码链接

你可以在这里找到该案例的程序

这有一个进阶版的体绘制Demo

OpenGL可视化入门:体绘制(VRT)相关推荐

  1. 3dmax Vray建筑可视化入门学习教程

    面向初学者的3Ds Max Vray最佳Archviz可视化课程 从安装到最终图像的一切都将从头开始教授,不需要任何经验 大小解压后:3.25G 时长4h 6m 1280X720 MP4 语言:英语+ ...

  2. OpenGL从入门到精通--纹理

    纹理 github源码仓库 opengl环境准备 opengl编程从入门到精通-hello,window OpenGL从入门到精通–你好三角形 OpenGL从入门到精通–着色器的使用 我们可以为每个顶 ...

  3. OpenGL从入门到精通--着色器的使用

    着色器 github源码仓库 opengl环境准备 opengl编程从入门到精通-hello,window OpenGL从入门到精通–你好三角形 OpenGL从入门到精通–着色器的使用 着色器(Sha ...

  4. OpenGL从入门到精通--你好三角形

    三角形 github源码仓库 opengl环境准备 opengl编程从入门到精通-hello,window OpenGL从入门到精通–你好三角形 OpenGL从入门到精通–着色器的使用 绘图中需要牢记 ...

  5. opengl从入门到精通

    Hello opengl github源码仓库 opengl环境准备 opengl编程从入门到精通-hello,window OpenGL从入门到精通–你好三角形 OpenGL从入门到精通–着色器的使 ...

  6. OpenGL ES入门(使用指南)

    转载地址:https://www.ict528.com/wpozv3sz3srrtywpoq1qvuyqooqxz1usvwr2uqoo.html. OpenGL ES 入门 一.前言 OpenGL ...

  7. OpenGL编程入门学习

    OpenGL编程入门学习  非常详细的教程,很适合初学者 本文转自:http://www.cppblog.com/doing5552/archive/2009/01/08/71532.html === ...

  8. OpenGL快速入门 1

    OpenGL快速入门 opengl 发布于 2018-12-06   约 79 分钟 概述 OpenGL OpenGL是渲染2D.3D矢量图形硬件的一种软件接口.本质上说,它是一个3D图形和模型库,具 ...

  9. OpenGL ES 入门之旅--灰度,旋涡,马赛克滤镜

    前情提要 这篇滤镜效果的实现是在上一篇分屏滤镜的基础上来进行实现的,同样的前提是可以利用GLSL加载一张正常的图片. 详情请参考上一篇OpenGL ES 入门之旅--分屏滤镜 下面步入这篇的正题: 灰 ...

最新文章

  1. java 内存溢出-与gc
  2. win10 如何打开telnet,ftp等服务
  3. java 实现栈_栈的Java实现
  4. webpack4.x 模块化浅析-CommonJS 1
  5. 参考的datalist分页helper
  6. 双光子荧光成像_在不影响分辨率的情况下,成功将双光子显微镜成像速度提高5倍!...
  7. RVC使用指南(二)-集群管理
  8. mongodb的体系
  9. 【Vue】—动态组件
  10. php 微信转发朋友圈,php微信分享到朋友圈、QQ、朋友、微博
  11. python自动填表单_用pythonwebdriver实现自动填表
  12. 计算机网络(谢希仁版)知识点汇总
  13. android 获取刘海高度,不同刘海屏幕获取安全高度
  14. 制作歌词录入系统php,如何制作歌词字幕 制作字幕的软件
  15. [技术脑洞] 如果把14亿中国人拉到一个微信群里技术上能实现吗?
  16. ACS 中PEAP的认证
  17. OpenCV.反阈值二值化
  18. 微信支付-小程序支付全流程
  19. 简单的图片识别,源代码
  20. Vue-element tab选项卡二级页面返回缓存选中页签

热门文章

  1. 美团-轻食餐饮发展指南
  2. bootstrap - selectree树形结构下拉框
  3. Android 入门第二讲03-约束布局ConstraintLayout(可视化介绍,Chains链,MATCH_CONSTRAIN,百分比布局,圆形定位,Guideline,Barrier)
  4. python 入门零碎知识点
  5. 【无标题】C++错题本
  6. Jane Austen谈Python:文学与技术的交集
  7. Ubuntu16.04(Xenial Xerus 好客的非洲地松鼠)更换pip源
  8. 计算机科学导论第五章计算机组成课后答案,计算机科学导论第五章计算机组成.ppt...
  9. 优质开源:共享图书小程序3.0 全新UI 免费下载
  10. 《Web安全之机器学习入门》笔记:第七章 7.6朴素贝叶斯检测DGA域名