osg 使用整理 (7):体渲染效果
osg 使用整理 (7):体渲染效果
体渲染技术可用于医学成像、计算流体力学、有限元、地球物理学、遥感等领域,数据通常来源于CT扫描、核磁共振MRI、卫星成像和声纳等设备,大概分为三种:直接体渲染技术(光线投射法、抛雪球法、错切变形法)、间接体绘制技术和最大密度投影技术。
1 光线投射法(ray casting)
re:《GPU编程与CG语言之阳春白雪下里巴人》
光线投射法是基于图像序列的直接体绘制算法。从图像的每一个像素,沿着视线方向发射一条光线,光线穿越整个图像序列,并在这个过程中,对图像序列进行采样获取颜色信息,同时依据光线吸收模型将颜色值进行累加,直至光线穿越整个图像序列,最后得到的颜色值就是渲染图像的颜色。
1.1 投射方向
首先创建立方体几何,作为体纹理载体,体纹理通过三维纹理坐标和模型一一对应,然后由视点向模型上的点连射线,该射线穿越模型空间等价于射线穿越了体纹理,需要注意OpenGL和Direct3D使用的体纹理坐标并不相同。
射线起始点为相机位置,终止于立方体背面点,射线方向由此求出。开启OpengGL正面剔除设置:
glEnable(GL_CULL_FACE); glCullFace(GL_BACK);//GL_BACK 背面 GL_FRONT 正面 GL_FRONT_AND_BACK 正面和反面
穿越体数据过程中做等距采样,采样密度由外界设置,根据视点和出射点计算投射距离,除以采样密度得到采样次数,这样在每一次采样循环过程中按照颜色合成公式进行反复累加。
1.2 透明度合成公式
透明度代表光穿透物体的能力,如果穿透多个物体,则这种变化是累加的,称为alpha混合技术。如果有多个透明物体,通常需要对物体进行排序。从背面到前面进行采样合成的公式为:
直到透明度累加超过1或者采样距离大于投射距离或者采样次数超过设置最大采样次数时,结束颜色混合迭代。
1.3 osg中光线投射法shader实现
顶点着色器代码:
#version 330 layout(location = 0) in vec3 Postion; layout(location = 1) in vec4 Color; layout(location = 8) in vec3 TexCoord; uniform mat4 osg_ModelViewProjectionMatrix; uniform mat4 osg_ViewMatrixInverse; uniform mat4 toTextureMatrix; out vec4 cameraPos; out vec4 vertexPos; out vec4 baseColor; out vec3 lightDir; void main(void) { gl_Position=osg_ModelViewProjectionMatrix*vec4(Postion,1.0); cameraPos = toTextureMatrix*osg_ViewMatrixInverse * vec4(0,0,0,1); vertexPos=vec4(TexCoord,1.0); baseColor=Color; vec4 lightPosition = osg_ViewMatrixInverse * gl_LightSource[0].position; if (lightPosition[3]==0.0) { lightDirection = -normalize(lightPosition.xyz); } else { lightDirection = normalize((lightPosition-vertexPos).xyz); } }
片段着色器代码 (带颜色转移函数)
#version 330 uniform sampler3D baseTexture; uniform sampler1D tfTexture; uniform float tfScale; uniform float tfOffset; uniform float SampleDensityValue; uniform float TransparencyValue; uniform float AlphaFuncValue; in vec4 cameraPos; in vec4 vertexPos; in vec4 baseColor; out vec4 FragColor; void main(void) { vec4 t0 = vertexPos; vec4 te = cameraPos; if (te.x>=0.0 && te.x<=1.0 && te.y>=0.0 && te.y<=1.0 && te.z>=0.0 && te.z<=1.0) { // do nothing... te inside volume } else { if (te.x<0.0) { float r = -te.x / (t0.x-te.x); te = te + (t0-te)*r; } if (te.x>1.0) { float r = (1.0-te.x) / (t0.x-te.x); te = te + (t0-te)*r; } if (te.y<0.0) { float r = -te.y / (t0.y-te.y); te = te + (t0-te)*r; } if (te.y>1.0) {\n" float r = (1.0-te.y) / (t0.y-te.y); te = te + (t0-te)*r; } if (te.z<0.0) { float r = -te.z / (t0.z-te.z); te = te + (t0-te)*r; } if (te.z>1.0) { float r = (1.0-te.z) / (t0.z-te.z); te = te + (t0-te)*r; } } const float min_iteratrions = 2.0; const float max_iteratrions = 2048.0; float num_iterations = ceil(length((te-t0).xyz)/SampleDensityValue); if (num_iterations<min_iteratrions) num_iterations = min_iteratrions; else if (num_iterations>max_iteratrions) num_iterations = max_iteratrions; vec3 deltaTexCoord=(t0-te).xyz/float(num_iterations-1.0); vec3 texcoord = te.xyz; vec4 fragColor = vec4(0.0, 0.0, 0.0, 0.0); while(num_iterations>0.0) { float v = texture( baseTexture, texcoord).a * tfScale + tfOffset; vec4 color = texture1D( tfTexture, v); float r = color[3]*TransparencyValue; if (r>AlphaFuncValue) { fragColor.xyz = fragColor.xyz*(1.0-r)+color.xyz*r; fragColor.w += r; } if(fragColor.w>1.0)break; texcoord += deltaTexCoord; --num_iterations; } fragColor.w *= TransparencyValue; if (fragColor.w>1.0) fragColor.w = 1.0; fragColor *= baseColor; if (fragColor.w<AlphaFuncValue) discard; FragColor = fragColor; };
iso等值面片段着色器
#version 330 uniform sampler3D baseTexture; uniform sampler1D tfTexture; uniform float tfScale; uniform float tfOffset; uniform float SampleDensityValue; uniform float TransparencyValue; uniform float IsoSurfaceValue; in vec4 cameraPos; in vec4 vertexPos; in vec4 baseColor; in vec3 lightDirection; out vec4 FragColor; void main(void) { vec4 t0 = vertexPos; vec4 te = cameraPos; if (te.x>=0.0 && te.x<=1.0 && te.y>=0.0 && te.y<=1.0 && te.z>=0.0 && te.z<=1.0) { // do nothing... te inside volume } else { if (te.x<0.0) { float r = -te.x / (t0.x-te.x); te = te + (t0-te)*r; } if (te.x>1.0) { float r = (1.0-te.x) / (t0.x-te.x); te = te + (t0-te)*r; } if (te.y<0.0) { float r = -te.y / (t0.y-te.y); te = te + (t0-te)*r; } if (te.y>1.0) {\n" float r = (1.0-te.y) / (t0.y-te.y); te = te + (t0-te)*r; } if (te.z<0.0) { float r = -te.z / (t0.z-te.z); te = te + (t0-te)*r; } if (te.z>1.0) { float r = (1.0-te.z) / (t0.z-te.z); te = te + (t0-te)*r; } } const float min_iteratrions = 2.0; const float max_iteratrions = 2048.0; float num_iterations = ceil(length((te-t0).xyz)/SampleDensityValue); if (num_iterations<min_iteratrions) num_iterations = min_iteratrions; else if (num_iterations>max_iteratrions) num_iterations = max_iteratrions; vec3 deltaTexCoord=(t0-te).xyz/float(num_iterations-1.0); vec3 texcoord = te.xyz; float previousV = texture( baseTexture, texcoord).a; float normalSampleDistance = 1.0/512.0; vec3 deltaX = vec3(normalSampleDistance, 0.0, 0.0); vec3 deltaY = vec3(0.0, normalSampleDistance, 0.0); vec3 deltaZ = vec3(0.0, 0.0, normalSampleDistance); vec4 fragColor = vec4(0.0, 0.0, 0.0, 0.0); while(num_iterations>0.0) { float v = texture3D( baseTexture, texcoord).a; float m = (previousV-IsoSurfaceValue) * (v-IsoSurfaceValue); if (m <= 0.0) { float r = (IsoSurfaceValue-v)/(previousV-v); texcoord = texcoord - r*deltaTexCoord; v = texture( baseTexture, texcoord).a * tfScale + tfOffset; vec4 color = texture( tfTexture, v); float px = texture( baseTexture, texcoord + deltaX).a; float py = texture( baseTexture, texcoord + deltaY).a; float pz = texture( baseTexture, texcoord + deltaZ).a; float nx = texture( baseTexture, texcoord - deltaX).a; float ny = texture( baseTexture, texcoord - deltaY).a; float nz = texture( baseTexture, texcoord - deltaZ).a; vec3 grad = vec3(px-nx, py-ny, pz-nz); if (grad.x!=0.0 || grad.y!=0.0 || grad.z!=0.0) { vec3 normal = normalize(grad); float lightScale = 0.1 + max(0.0, dot(normal.xyz, lightDirection))*0.9; color.x *= lightScale; color.y *= lightScale; color.z *= lightScale; } color *= baseColor; FragColor = color; return ; } previousV=v; texcoord += deltaTexCoord; --num_iterations; } discard; };
light光照片段着色器
#version 330 uniform sampler3D baseTexture; uniform sampler1D tfTexture; uniform float tfScale; uniform float tfOffset; uniform float SampleDensityValue; uniform float TransparencyValue; uniform float AlphaFuncValue; in vec4 cameraPos; in vec4 vertexPos; in vec4 baseColor; in vec3 lightDirection; out vec4 FragColor; void main(void) { vec4 t0 = vertexPos; vec4 te = cameraPos; if (te.x>=0.0 && te.x<=1.0 && te.y>=0.0 && te.y<=1.0 && te.z>=0.0 && te.z<=1.0) { // do nothing... te inside volume } else { if (te.x<0.0) { float r = -te.x / (t0.x-te.x); te = te + (t0-te)*r; } if (te.x>1.0) { float r = (1.0-te.x) / (t0.x-te.x); te = te + (t0-te)*r; } if (te.y<0.0) { float r = -te.y / (t0.y-te.y); te = te + (t0-te)*r; } if (te.y>1.0) {\n" float r = (1.0-te.y) / (t0.y-te.y); te = te + (t0-te)*r; } if (te.z<0.0) { float r = -te.z / (t0.z-te.z); te = te + (t0-te)*r; } if (te.z>1.0) { float r = (1.0-te.z) / (t0.z-te.z); te = te + (t0-te)*r; } } const float min_iteratrions = 2.0; const float max_iteratrions = 2048.0; float num_iterations = ceil(length((te-t0).xyz)/SampleDensityValue); if (num_iterations<min_iteratrions) num_iterations = min_iteratrions; else if (num_iterations>max_iteratrions) num_iterations = max_iteratrions; vec3 deltaTexCoord=(t0-te).xyz/float(num_iterations-1.0); vec3 texcoord = te.xyz; float normalSampleDistance = 1.0/512.0; vec3 deltaX = vec3(normalSampleDistance, 0.0, 0.0); vec3 deltaY = vec3(0.0, normalSampleDistance, 0.0); vec3 deltaZ = vec3(0.0, 0.0, normalSampleDistance); vec4 fragColor = vec4(0.0, 0.0, 0.0, 0.0); while(num_iterations>0.0) { float v = texture( baseTexture, texcoord).a * tfScale + tfOffset; vec4 color = texture1D( tfTexture, v); float a=v; float px = texture( baseTexture, texcoord + deltaX).a; float py = texture( baseTexture, texcoord + deltaY).a; float pz = texture( baseTexture, texcoord + deltaZ).a; float nx = texture( baseTexture, texcoord - deltaX).a; float ny = texture( baseTexture, texcoord - deltaY).a; float nz = texture( baseTexture, texcoord - deltaZ).a; vec3 grad = vec3(px-nx, py-ny, pz-nz); if (grad.x!=0.0 || grad.y!=0.0 || grad.z!=0.0) { vec3 normal = normalize(grad); float lightScale = 0.1 + max(0.0, dot(normal.xyz, lightDirection))*0.9; color.x *= lightScale; color.y *= lightScale; color.z *= lightScale; } float r = color[3]*TransparencyValue; if (r>AlphaFuncValue) { fragColor.xyz = fragColor.xyz*(1.0-r)+color.xyz*r; fragColor.w += r; } if (fragColor.w<color.w) { fragColor = color; } texcoord += deltaTexCoord; --num_iterations; } fragColor.w *= TransparencyValue; if (fragColor.w>1.0) fragColor.w = 1.0; fragColor *= baseColor; if (fragColor.w<AlphaFuncValue) discard; FragColor = fragColor; };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具