OSG OIT 順序無關透明繪製(PPLL_OIT, WB_OIT) 實現及注意事項

 

這段時間研究了一下順序無關的半透明實體繪製實現方法,順便完善了OSG顯示引擎半透明實體繪製部分,看了看相關資料,正確性、效率最好的是基於權重函數的混合算法和GPU端鏈表法,還有個基於矩的數學方法,繪製的效果比基於權重函數的混合算法效果更好,太複雜,俺也懶得看了,看也看不懂,只有DX代碼,放棄了。兩種方法存在的問題羅列一下,給其他有興趣實現的OIT透明繪製的朋友做個參考,本人比較水:)。先來張我心中的女神的圖片,內衣是基於權重混合函數實現的:)---,(我自己都覺得自己是藝術家了,看什麼都心靜如水:)。。。,我老婆說這些美女(還有幾個沒穿衣服的)皮膚太油不真實(皮膚繪製也是個課題,難點),她也沒怪我拿個裸體模型天天在她眼前顯擺)。

 

WB OIT
WB OIT

 

WB OIT

     內內特寫:)----, 這就是推動俺不斷學習的動力,比看倉老師的片子還有成就感。

WB OIT

 

    再放個車的圖片,香車美女。。。

 

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()權重函數。一般我們認爲離的近的半透明物體罩着後面的物體,看上去顏色也最貼最前面的物體的顏色,論文裏幾個效果比較好的權重公式:

照公式套,實現上還是比較簡單,基於加權平均和權重的混合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<osg::UIntArray> atomicCounterArray = new osg::UIntArray;
	atomicCounterArray->push_back(0);
	osg::ref_ptr<osg::AtomicCounterBufferObject> acbo = new osg::AtomicCounterBufferObject;
	acbo->setUsage(GL_STREAM_COPY);
	atomicCounterArray->setBufferObject(acbo.get());
	osg::ref_ptr<osg::AtomicCounterBufferBinding> 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<osg::UIntArray> linked_list_buffer = new osg::UIntArray;
	osg::ref_ptr<osg::PixelDataBufferObject> 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<osg::TextureBuffer> 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 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章