OpenGL数据可视化(3)绘制三维地震切片图

通过 OpenGL 的三维纹理实现 3D 地震数据的 3 方向切片显示,系列代码已开源。

开源链接:opengl_practice (github.com)

2D 纹理实现回顾

回顾 2D 纹理的实现,是画了两个三角形,然后给两个三角形贴上对应位置的纹理,相关代码:

float vertices[] = {
    // positions          // texture coords
    1.0f,  1.0f, 0.0f,   1.0f, 1.0f, // top right
    1.0f, -1.0f, 0.0f,   1.0f, 0.0f, // bottom right
   -1.0f, -1.0f, 0.0f,   0.0f, 0.0f, // bottom left
   -1.0f,  1.0f, 0.0f,   0.0f, 1.0f  // top left 
    };
unsigned int indices[] = {
    0, 1, 3, // first triangle
    1, 2, 3  // second triangle
};

指定顶点数组 vertices 中前 3 个值为三角形顶点的空间坐标,后 2 个值为纹理坐标:

// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

按照 indices 从vertices 中取顶点画三角形,如果将 indices 改为:

unsigned int indices[] = {
    0, 1, 3, // first triangle
    //1, 2, 3  // second triangle
};

此时运行结果就只有第一个三角形了:

3D 切片实现

3D 纹理不只有宽度和高度,还有深度,其最大宽度和高度与 2D 纹理,最大深度可通过查询 GL_MAX_3D_TEXTURE_SIZE 的值来获取,我这里的值是 2048。

3D 纹理的实现与 2D 纹理相似,此时顶点数组中纹理坐标需要有 3 个值,下面只展示 y-z 切片的顶点数组:

float vertices[] = {
    // positions          // texture coords
     0.0f,  1.0f,  1.0f,  0.5f, 1.0f, 1.0f, // top right
     0.0f,  1.0f, -1.0f,  0.5f, 1.0f, 0.0f, // bottom right
     0.0f, -1.0f,  1.0f,  0.5f, 0.0f, 1.0f, // top left 
     0.0f,  1.0f, -1.0f,  0.5f, 1.0f, 0.0f, // bottom right
     0.0f, -1.0f, -1.0f,  0.5f, 0.0f, 0.0f, // bottom left
     0.0f, -1.0f,  1.0f,  0.5f, 0.0f, 1.0f, // top left
};

指定顶点数组 vertices 中前 3 个值为三角形顶点的空间坐标,后 3 个值为纹理坐标:

// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

和 2D 纹理不同,这里我用 6 组坐标画 2 个三角形,没有用 indices 来复用坐标,两种方法都可以。

注意,在顶点着色器和片段着色器中,也需要将纹理坐标改为 3 个。

最后效果:

 让切片动起来

能否通过设置切片位置,让切片自动滑动呢?是可以实现的。

在 GLSL 中,uniform 变量可以将外部程序中的变量传递给着色器。可以发现,上一小节顶点数组中,纹理坐标与顶点的空间坐标存在线性关系:空间坐标范围是 [-1, 1],纹理坐标范围是 [0, 1],故 (空间坐标 + 1) / 2 = 纹理坐标。

实际上,纹理坐标的范围必须是0~1,而顶点空间坐标可以根据需要设置。

由于纹理坐标可由空间坐标得到,将切片位置设置为 uniform 变量,那么 y-z 切片的顶点数组可以写为:

float vertices[] = {
    // index //positions
    0,  1.0f,  1.0f,
    0,  1.0f, -1.0f,
    0, -1.0f,  1.0f,
    0,  1.0f, -1.0f,
    0, -1.0f, -1.0f,
    0, -1.0f,  1.0f,
}

第一个值为与切片垂直的轴,即切片移动的轴,0 表示 y-z 切片,即 x 轴。

指定顶点数组 vertices 中前 1 个值为切片移动的轴,后 2 个值为三角形顶点空间坐标:

// axis
glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// position attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(1 * sizeof(float)));
glEnableVertexAttribArray(1);

 以 y-z 切片为例,顶点着色器部分代码:

uniform float id0;

void main()
{
	vec2 aTexCoord = (aPos+1)/2;
	vec4 Pos;
	if (aAxis < 1){
		TexCoord = vec3(id0, 1-aTexCoord.x, aTexCoord.y);
		Pos = vec4(2*id0-1, aPos.x, aPos.y, 1.0f);
	}
	gl_Position = projection * view * model * Pos;
}

此处只展示 y-z 切片相关代码,完整代码需要将 3 方向切片都写入 if else 中。最开始我是用 switch 语句,但是那样只显示 aAxis=0 的切片。如有更好的方法,还望读者赐教。

最终效果(切片随时间移动,这里将 5 个时刻的图像拼接显示):

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值