OSG OIT 顺序无关透明绘制(PPLL_OIT, WB_OIT) 实现及注意事项

 

这段时间研究了一下顺序无关的半透明实体绘制实现方法,顺便完善了OSG显示引擎半透明实体绘制部分,看了看相关资料,正确性、效率最好的是基于权重函数的混合算法和GPU端链表法,还有个基于矩的数学方法,绘制的效果比基于权重函数的混合算法效果更好,太复杂,俺也懒得看了,看也看不懂,只有DX代码,放弃了。两种方法存在的问题罗列一下,给其他有兴趣实现的OIT透明绘制的朋友做个参考,本人比较水:)。先来张我心中的女神的图片,内衣是基于权重混合函数实现的:)---,(我自己都觉得自己是艺术家了,看什么都心静如水:)。。。,我老婆说这些美女(还有几个没穿衣服的)皮肤太油不真实(皮肤绘制也是个课题,难点),她也没怪我拿个裸体模型天天在她眼前显摆)。

 

OSG OIT 顺序无关透明绘制(PPLL_OIT, WB_OIT) 实现及注意事项_第1张图片 WB OIT

 

OSG OIT 顺序无关透明绘制(PPLL_OIT, WB_OIT) 实现及注意事项_第2张图片 WB OIT

     内内特写:)----, 这就是推动俺不断学习的动力,比看仓老师的片子还有成就感。

OSG OIT 顺序无关透明绘制(PPLL_OIT, WB_OIT) 实现及注意事项_第3张图片 WB OIT

OSG OIT 顺序无关透明绘制(PPLL_OIT, WB_OIT) 实现及注意事项_第4张图片

 

    再放个车的图片,香车美女。。。

 

WB OIT

下面简单说说算法公式,其实不知原理,套公式一样能实现,只是不明白原理的话,出了问题浪费点精气神,好在公式简单明了,如果想了解更详细些,搜一下大神的论文Weighted Blended Order-Independent Transparency,就是G3D 革新引擎的作者 Morgan McGuire,这个家伙是个牛人,Nvidia 的人。

  •  Weighted Blended OIT

   算法的演进:

第一版 Meshkin 2007 年的首先提出的 Sort-independent alpha blending 论文,公式如下

简单明了,   , C0 是背景颜色, 从该公式可以看出他只是简单的将源颜色求和 ,再加上 目标颜色 * (1-源颜色a值求和) 做为混合后的最终颜色,该公式也不是人家瞎整,虽然不具备通用性,但对于颜色相近和a值较小的情况下效果最好,公式推断可以看看论文,如何把顺序相关的因子排除掉,为后续 平均加权 OIT 方法奠定了坚实的基础。

第二版 Bavoil and Myers 的加权平均法 ,也算是对上一方法的改进版,公式如下

该公式基于加权求和的方式对 Meshkin 的方法做了改进,正确性提高了很多,而且更具有通用性,但当a 为 0 的时候,本来应该不贡献颜色的片元也参与了加权求和,使颜色变淡,总是透明的。该公式也有个缺点,后续版本也一样,如果不透明的实体使用该公式绘制,C0项目 为0, 但公式前半部分 变为 Ci 求和项 除于 ai 求和项,  平均了颜色,所以明明不透明的实体也变得透明了。

第三版 由 Morgan McGuire 2013 年提出,对上述公式加以改进,解决了a 为0 ,红字部分没解决。公式如下:

对a 求和改成 乘法了,全透明实体的绘制问题解决了, 该公式考虑到 片元深度和 a 的影响 ,加入了w()权重函数。一般我们认为离的近的半透明物体罩着后面的物体,看上去颜色也最贴最前面的物体的颜色,论文里几个效果比较好的权重公式:

OSG OIT 顺序无关透明绘制(PPLL_OIT, WB_OIT) 实现及注意事项_第5张图片

照公式套,实现上还是比较简单,基于加权平均和权重的混合OIT实现方法,无非是求和,平均。

下面我给出我的实现代码, demo 程序就不给了,代码粘贴拷贝很容易 使用OSG后处理来实现。

void WB_OITRenderPass::initialize()
{
	_pass = new osg::Group();
	_pass->setName("WB_OIT");

	_accumTexture = createTexture2D(getFrameBufferWidth(), getFrameBufferHeight(), GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT);
	_accumTexture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE);
	_accumTexture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE);
	_accumTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST);
	_accumTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST);

	_accumAlphaTexture = createTexture2D(getFrameBufferWidth(), getFrameBufferHeight(), GL_R16F, GL_RED, GL_FLOAT);
	_accumAlphaTexture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE);
	_accumAlphaTexture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE);
	_accumAlphaTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST);
	_accumAlphaTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST);

	// Accum pass.
	_accumPass = createRTTCamera(getFrameBufferWidth(), getFrameBufferHeight(), false, GL_COLOR_BUFFER_BIT);
	_accumPass->setName("WB_OIT_AccumPass");
	_accumPass->attach(osg::Camera::COLOR_BUFFER0, _accumTexture);
	_accumPass->attach(osg::Camera::COLOR_BUFFER1, _accumAlphaTexture);
	_accumPass->attach(osg::Camera::DEPTH_BUFFER, getContext()->_depthBuffer);
	_accumPass->addChild(getContext()->getPipeline()->getSceneRoot());
	_accumPass->setCullCallback(new PassCallback(getContext()->getPipeline()));
	_accumPass->setClearColor(osg::Vec4(0.0, 0.0, 0.0, 1.0));

	osg::StateSet* ss = setShaderProgram(_accumPass, "trans_accum", osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
	ss->setMode(GL_CULL_FACE,  osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);

	osg::Depth* depth = new osg::Depth;
	depth->setFunction(osg::Depth::LEQUAL);
	depth->setWriteMask(false);
	ss->setAttributeAndModes(depth, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
	
	osg::BlendFunc* bf = new osg::BlendFunc(osg::BlendFunc::ONE, osg::BlendFunc::ONE, osg::BlendFunc::ZERO, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
	ss->setAttributeAndModes(bf, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);

	// Draw pass
	_drawPass = createRTTCamera(getFrameBufferWidth(), getFrameBufferHeight(), true, GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
	_drawPass->attach(osg::Camera::COLOR_BUFFER, getContext()->_outputTextureWithLum);
	_drawPass->attach(osg::Camera::DEPTH_BUFFER, getContext()->_tempDepthBuffer);
	_drawPass->setName("WB_OIT_DrawPass");

	ss = setShaderProgram(_drawPass, "trans_draw");

	ss->setTextureAttributeAndModes(0, _accumTexture);
	ss->addUniform(new osg::Uniform("Accumulate", 0));

	ss->setTextureAttributeAndModes(1, _accumAlphaTexture);
	ss->addUniform(new osg::Uniform("AccumulateAlpha", 1));

	ss->setTextureAttributeAndModes(2, getContext()->_outputTexture);
	ss->addUniform(new osg::Uniform("Opacity", 2));
	
	_pass->addChild(_accumPass);
	_pass->addChild(_drawPass);

	getContext()->addPass(this);
}

shader 部分代码:

#version 420 core
 
in vec4 osg_Vertex;
in vec3 osg_Normal;
in vec4 osg_MultiTexCoord0;

uniform mat4 osg_ViewMatrix;
uniform mat4 osg_ViewMatrixInverse;
uniform mat4 osg_ModelViewMatrix;
uniform mat4 osg_ModelViewProjectionMatrix;

out vec2 TexCoords;
out vec3 WorldPos;
out vec3 WorldNormal;

void main()
{
    TexCoords   = osg_MultiTexCoord0.xy;  

    mat4 worldMatrix = osg_ViewMatrixInverse * osg_ModelViewMatrix;
    WorldPos = (worldMatrix * osg_Vertex).xyz;
          
    mat3 normalMatrix = mat3(worldMatrix);
    WorldNormal = normalize(normalMatrix * osg_Normal); 
          
    gl_Position = osg_ModelViewProjectionMatrix * osg_Vertex;
}

#version 420 core
 
#extension GL_ARB_shader_image_load_store : enable
layout (early_fragment_tests) in;
 
#include "chunk_math.glsl"
#include "forward_pbr_shading_parameters.frag"
#include "chunk_shadowmap.frag"
#include "chunk_light.frag"
#include "tone_mapping.frag"

 float weight(float z, float a) 
 {
	return clamp(pow(min(1.0, a * 10.0) + 0.01, 3.0) * 1e8 * pow(1.0 - z * 0.9, 3.0), 1e-2, 3e3);
 }
 
vec4 shading()
{

	vec4  _albedo    = getAlbedo();
	float _roughness = getRoughness();
	float _metallic  = getMetallic();
	float _ao        = getAo();
	vec3 camPos      = getCameraPosition();
	vec3 worldNormal = normalize(getWorldNormal());
	vec4 worldPos    = getWorldPosition();
	
	float _ssao      = 1.0;
	float _shadow    = 0.0;
	
	#if !defined(MATERIAL_IS_TRANSPARENT) && !defined(MAP_IS_TRANSPARENT)
		if(transparency < 0.0001)
		{
			_ssao   = getSSAO();
			_shadow = getShadow(vec4(WorldPos,1.0), WorldNormal);
		}
	#endif
	
	vec3 F0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + _albedo.rgb * metallic;
	vec3 diffuse = _albedo.rgb * (1.0 - metallic);
	
	vec3 Lo = vec3(0.0);
    vec3 ambient = vec3(0.0);
	
#ifdef NUMBER_LIGHTS
	for (int i = 0; i < NUMBER_LIGHTS; ++i)
	{
		Light light = Lights[i];
		Lo += CalcPointOrDirectionalLight( light, camPos, worldPos.xyz, worldNormal, F0, diffuse, metallic, roughness, ambient );
	}
#endif

	//Ambient lighting 
	#ifdef IRRADIANCEMAP
		vec3 kS = fresnelSchlick(max(dot(N, V), 0.0), F0);
		vec3 kD = 1.0 - kS;
		kD *= 1.0 - _metallic;	  
		vec3 irradiance = texture(irradianceMap, N).rgb;
		vec3 diffuse      = irradiance * _albedo;
		ambient = (kD * diffuse) * _ao;
	#else
		ambient = ambient * _albedo.rgb * _ao;
	#endif
	
	Lo = Lo*( 1.0 - _shadow);
	vec3 color = ambient* _ssao + Lo;
	
	#ifdef IRRADIANCEMAP
          //vec3 I = normalize(WorldPos - camPos);
          //vec3 R = reflect(I, normalize(WorldNormal));
          //color = texture(irradianceMap, R).rgb;
		  //color = texture(irradianceMap,TexCoords2).rgb;
	#endif
	
	color = tonemap(color);
	
	vec4 outputColor = vec4(color, _albedo.a);
	
	return outputColor;
}

layout (location = 0) out vec4 out_accumColor;
layout (location = 1) out float out_accumAlpha;

void main()
{		
     vec4 color = shading();
     color.rgb *= color.a;
     float w = weight(gl_FragCoord.z, color.a);
     out_accumColor = vec4(color.rgb * w, color.a);
     out_accumAlpha = color.a * w;
}
#version 420 core

in vec4 osg_MultiTexCoord0;
in vec4 osg_Vertex;

uniform mat4 osg_ModelViewProjectionMatrix;

out vec2 TexCoord;

void main()
{
	TexCoord    = osg_MultiTexCoord0.xy;
	gl_Position = osg_ModelViewProjectionMatrix * osg_Vertex;
}
#version 420 core

#include "chunk_math.glsl"

in vec2 TexCoord;

uniform sampler2D Accumulate;
uniform sampler2D AccumulateAlpha;
uniform sampler2D Opacity;

layout (location = 0) out vec4 fragColor;

void main()
{    	 
	 ivec2 fragCoord = ivec2(gl_FragCoord.xy);
	 
     vec4 accum = texelFetch(Accumulate, fragCoord, 0);
	 float r = accum.a;
     accum.a = texelFetch(AccumulateAlpha, fragCoord, 0).r;
	 
     vec4 color = vec4(accum.rgb / clamp(accum.a, 0.0001, 50000.0), r);	
	 color.rgb = pow(color.rgb, vec3(1.0/2.2));
	 
	 vec4 opaqueColor = texelFetch(Opacity, fragCoord, 0).rgba;
	 vec3 outputColor = mix(color.rgb, opaqueColor.rgb, color.a);

     //luminance 为实现FXAA反走样计算亮度值,如果只是测试,a为 1 就可以了。
	 fragColor = vec4(outputColor, luminance(outputColor));
}

 
   

结论: Weighted Blended OIT ,效果还过得去,比非OIT 绘制正确性合理的多,但毕竟是去掉了公式中顺序相关项推出来的透明融合,存在一些缺陷:

1) 不透明实体被绘制成透明实体,见上文红字部分,如果纹理带a通道,透明通道内不能实现镂空效果。

2) 绘制的不是十分正确,只是整体上过的去;

 

  • GPU 端链表法 实现OIT  

直接上源码,至于实现原理过程,OpenGL 编程指南介绍的很清楚了,最后说下该方法存在的问题:

 

程序初始化部分,这部分代码最有价值的是 OSG 原子计数器, TBO 存储的实现。很庆幸OSG 虽然没落了,但API更新还算及时,支持 计算,几何着色器, 原子操作, TBO 等等。

void PPLL_OITPass::initialize()
{
	_OITRoot = new osg::Group();

	_FinalOIT = createTexture2D(getViewportWidth(), getViewportHeight(), GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
	_FinalOIT->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE);
	_FinalOIT->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE);
	_FinalOIT->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
	_FinalOIT->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);

	//Head pointer texture.
	_head_pointer_texture = createTexture2D(getViewportWidth(), getViewportHeight(), GL_R32UI, GL_RED_INTEGER_EXT, GL_UNSIGNED_INT);
	_head_pointer_texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE);
	_head_pointer_texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE);
	_head_pointer_texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST);
	_head_pointer_texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST);
	_head_pointer_image = new osg::BindImageTexture(0,
		_head_pointer_texture,
		osg::BindImageTexture::READ_WRITE, GL_R32UI,
		0,
		false,
		0);

    // 原子计数器创建部分
	osg::ref_ptr atomicCounterArray = new osg::UIntArray;
	atomicCounterArray->push_back(0);
	osg::ref_ptr acbo = new osg::AtomicCounterBufferObject;
	acbo->setUsage(GL_STREAM_COPY);
	atomicCounterArray->setBufferObject(acbo.get());
	osg::ref_ptr acbb = new osg::AtomicCounterBufferBinding(0, atomicCounterArray.get(), 0, sizeof(GLuint));
	acbb->setUpdateCallback(new ResetAtomicCounter);

	//创建链表
#define OIT_LAYERS  3
	int linked_list_buffer_item_size = 2048 * 2048 * OIT_LAYERS;
	osg::ref_ptr linked_list_buffer = new osg::UIntArray;
	osg::ref_ptr pdbo = new osg::PixelDataBufferObject();
	pdbo->setUsage(GL_DYNAMIC_COPY);
	pdbo->setTarget(GL_TEXTURE_BUFFER);
	pdbo->setDataSize(linked_list_buffer_item_size * sizeof(GLuint) * 4);
	linked_list_buffer->setBufferObject(pdbo);

	osg::ref_ptr tbo = new osg::TextureBuffer;
	tbo->setBufferData(linked_list_buffer);
	tbo->setInternalFormat(GL_RGBA32UI_EXT);
	osg::BindImageTexture* linked_list_image = new osg::BindImageTexture(1,
		tbo.get(),
		osg::BindImageTexture::WRITE_ONLY, GL_RGBA32UI_EXT,
		0,
		false,
		0);

    //每绘制一帧前,用前序渲染初始化链表纹理。红宝书里直接通过绑定PBO 操作初始化,我这里直接做一次离屏渲染。 
	_OITClearHeadPointerPass = createRTTCamera(getViewportWidth(), getViewportHeight(), true, GL_DEPTH_BUFFER_BIT);
	_OITClearHeadPointerPass->attach(osg::Camera::DEPTH_BUFFER, getContext()->_tempDepthBuffer);
	osg::StateSet* ss = setShaderProgram(_OITClearHeadPointerPass, "clear_head_pointer");
	ss->setAttribute(_head_pointer_image);

    //生成链表的过程。
	_OITPass = createRTTCamera(getViewportWidth(), getViewportHeight(), false, 0);
	_OITPass->setName("OIT_Trans_accum");
	_OITPass->attach(osg::Camera::DEPTH_BUFFER, getContext()->_depthBuffer);
	_OITPass->addChild(getContext()->getPipeline->getSceneRoot());
	_OITPass->setCullCallback(new PassCallback(getContext()->getPipeline()));
	_OITPass->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);

	ss = setShaderProgram(_OITPass, "build_lists", osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
	ss->setAttribute(_head_pointer_image);
	ss->setAttribute(linked_list_image);
	ss->setAttribute(acbb);
	ss->setMode(GL_BLEND, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
	ss->setMode(GL_CULL_FACE, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
	ss->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
	ss->addUniform(new osg::Uniform("itemCount", linked_list_buffer_item_size));

    //使用链表通过排序,混合生成最终图像。
	_OITDrawPass = createRTTCamera(getViewportWidth(), getViewportHeight(), true);
	_OITDrawPass->attach(osg::Camera::DEPTH_BUFFER, getContext()->_tempDepthBuffer);
	_OITDrawPass->attach(osg::Camera::COLOR_BUFFER0, _FinalOIT);
	ss = setShaderProgram(_OITDrawPass, "resolve_lists");
	ss->setAttribute(_head_pointer_image);
	ss->setAttribute(linked_list_image);
	ss->setTextureAttributeAndModes(0, getContext()->_outputTexture);
	ss->addUniform(new osg::Uniform("Final", 0));

	_OITRoot->addChild(_OITClearHeadPointerPass);
	_OITRoot->addChild(_OITPass);
	_OITRoot->addChild(_OITDrawPass);

	getContext()->addPass(this);
}

void PPLL_OITPass::uninitialize()
{
	getContext()->removePass(this);
}

 

#version 420 core
 
in vec4 osg_MultiTexCoord0;
in vec3 osg_Normal;
in vec4 osg_Vertex;
 
out vec2 TexCoords;
out vec3 WorldPos;
out vec3 WorldNormal;

uniform mat4 osg_ViewMatrixInverse;
uniform mat4 osg_ModelViewMatrix;
uniform mat4 osg_ViewMatrix;
uniform mat4 osg_ModelViewProjectionMatrix;

void main()
{
    TexCoords = osg_MultiTexCoord0.xy;  

    mat4 worldMatrix = osg_ViewMatrixInverse * osg_ModelViewMatrix;
    WorldPos = (worldMatrix * osg_Vertex).xyz;
          
    mat3 normalMatrix = mat3(worldMatrix);
    WorldNormal = normalize(normalMatrix * osg_Normal); 
          
    gl_Position = osg_ModelViewProjectionMatrix * osg_Vertex;
}
#version 420 core

layout (early_fragment_tests) in;
layout (binding = 0, r32ui) uniform uimage2D head_pointer_image;
layout (binding = 1, rgba32ui) uniform writeonly uimageBuffer list_buffer;
layout (binding = 0, offset = 0) uniform atomic_uint list_counter;

#include "chunk_math.glsl"
#include "forward_pbr_shading_parameters.frag"
#include "chunk_light.frag"
#include "tone_mapping.frag"

vec4 shading()
{		
	vec4  _albedo    = getAlbedo();
	float _roughness = getRoughness();
	float _metallic  = getMetallic();
	float _ao        = getAo();
	vec3 camPos      = getCameraPosition();
	vec4 worldPos    = getWorldPosition();
	
	vec3 F0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + _albedo.rgb * metallic;
	vec3 diffuse = _albedo.rgb * (1.0 - metallic);
	
	vec3 worldNormal;
	
	worldNormal = WorldNormal;

	vec3 Lo = vec3(0.0);
    vec3 ambient = vec3(0.0);
	
#ifdef NUMBER_LIGHTS
	for (int i = 0; i < NUMBER_LIGHTS; ++i)
	{
		Light light = Lights[i];
		Lo += CalcPointOrDirectionalLight( light, camPos, worldPos.xyz, worldNormal, F0, diffuse, metallic, roughness, ambient );
	}
#endif

	//Ambient lighting 
	#ifdef IRRADIANCEMAP
		vec3 kS = fresnelSchlick(max(dot(N, V), 0.0), F0);
		vec3 kD = 1.0 - kS;
		kD *= 1.0 - _metallic;	  
		vec3 irradiance = texture(irradianceMap, N).rgb;
		vec3 diffuse    = irradiance * _albedo;
		ambient = (kD * diffuse) * _ao;
	#else
		ambient = ambient * _albedo.rgb * _ao;
	#endif
	
	vec3 color = ambient + Lo;
	
	color = tonemap(color);
	
	//Gamma correction
	color = pow(color, vec3(1.0/2.2)); 

	#ifdef IRRADIANCEMAP
          //vec3 I = normalize(WorldPos - camPos);
          //vec3 R = reflect(I, normalize(WorldNormal));
          //color = texture(irradianceMap, R).rgb;
		  //color = texture(irradianceMap,TexCoords2).rgb;
	#endif
	
	return vec4(color, _albedo.a);
}

uniform int itemCount;

void main(void)
{
    uint index;
    uint old_head;
    uvec4 item;
	
    index = atomicCounterIncrement(list_counter) + 2;
	
	if(index > itemCount-1)
	{
	  index = 1;
	  old_head = 0;
	  imageAtomicExchange(head_pointer_image, ivec2(gl_FragCoord.xy), uint(index));
	  
	  item.x = old_head;
	  item.y = packUnorm4x8(vec4(1.0,0.0,0.0,1.0));
	  item.z = floatBitsToUint(gl_FragCoord.z);
	  imageStore(list_buffer, int(0), item);
	}
	else
	{
	    old_head = imageAtomicExchange(head_pointer_image, ivec2(gl_FragCoord.xy), uint(index));
		
		vec4 surface_color = shading();
		item.x = old_head;
		item.y = packUnorm4x8(surface_color);
		item.z = floatBitsToUint(gl_FragCoord.z);
		imageStore(list_buffer, int(index-1), item);
	}

}
#version 420 core

in vec4 osg_Vertex;
in vec4 osg_MultiTexCoord0;
uniform mat4 osg_ModelViewProjectionMatrix;

out vec2 TexCoord;

void main(void)
{
    TexCoord = osg_MultiTexCoord0.xy;
    gl_Position = osg_ModelViewProjectionMatrix * osg_Vertex;
}

#version 420 core

#pragma import_defines ( USE_FXAA )

#include "chunk_math.glsl"

// The per-pixel image containing the head pointers
layout (binding = 0, r32ui) uniform uimage2D head_pointer_image;
// Buffer containing linked lists of fragments
layout (binding = 1, rgba32ui) uniform uimageBuffer list_buffer;

// This is the output color
layout (location = 0) out vec4 finalColor;

// This is the maximum number of overlapping fragments allowed
#define MAX_FRAGMENTS 40

// Temporary array used for sorting fragments
uvec4 fragment_list[MAX_FRAGMENTS];

in vec2 TexCoord;
uniform sampler2D Final;

void main(void)
{
    int current_index;
    uint fragment_count = 0;

    current_index = int(imageLoad(head_pointer_image, ivec2(gl_FragCoord).xy).x) - 1;
	
    while (current_index >= 0 && fragment_count < MAX_FRAGMENTS)
    {
        uvec4 fragment = imageLoad(list_buffer, int(current_index));
        fragment_list[fragment_count] = fragment;
        current_index = int(fragment.x) -1;
        fragment_count++;
    }
	
    uint i, j;
    if (fragment_count > 1)
    {

        for (i = 0; i < fragment_count - 1; i++)
        {
            for (j = i + 1; j < fragment_count; j++)
            {
                uvec4 fragment1 = fragment_list[i];
                uvec4 fragment2 = fragment_list[j];

                float depth1 = uintBitsToFloat(fragment1.z);
                float depth2 = uintBitsToFloat(fragment2.z);

                if (depth1 < depth2)
                {
                    fragment_list[i] = fragment2;
                    fragment_list[j] = fragment1;
                }
            }
        }

    }
	
    vec3 backgroundColor = texture(Final, TexCoord.xy).rgb;
    for (i = 0; i < fragment_count; i++)
    {
        vec4 modulator  = unpackUnorm4x8(fragment_list[i].y);
        backgroundColor = mix(backgroundColor.rgb, modulator.rgb, modulator.a);
    }

	finalColor = vec4(backgroundColor, luminance(backgroundColor));
}

链表法OIT 无疑绘制半透明实体是最正确的,但它有不可克服的缺陷:

1)资源占用未可预知, 要预先分配,对复杂透明实体,层次比较深,很容易把链表预分配的内存资源吃尽,导致绘制不正确。链表一个项占用 64字节, 比如满屏2K屏,一个透明层占用 2048 *2048*64 bytes = 256M ;

2)物体缩小后会闪烁,放大后闪烁消失,不清楚是不是原子操作问题还是没有mipmap.

如果对正确性要求不是很高,基于权重混合的OIT 方法足够了,而且效率也比较高。

Demo 模型浏览器下载链接: https://pan.baidu.com/s/1H4lS-iKoTqroq6V-xiwpkw 提取码: f3ig 

你可能感兴趣的:(OpenSceneGraph,OpenGL,顺序无关透明,OSG,OIT,OSG,顺序无关透明,OSG,渲染,OpenGL,链表法顺序无关透明)