OpenGL ES SDK for Android - 3
Bloom
使用OpenGL ES 3.0的綻放效果。
綻放效果:強(qiáng)度從非常弱變?yōu)榉浅?qiáng)烈。
該應(yīng)用程序顯示了bloom實(shí)現(xiàn)。 它繪制了以二維5x5陣列排列的立方體,只有對(duì)角線的陣列才綻放。 綻放效果的強(qiáng)度從非常弱的變化到非常強(qiáng)。
綻放效果實(shí)現(xiàn)如下:
- 場景(5x5立方體陣列:對(duì)角線上的立方體為白色,其他為藍(lán)色)被繪制到渲染目標(biāo)。
- 應(yīng)該綻放的元素(更明亮的元素為放置在對(duì)角線上的立方體)被繪制到縮小尺寸的紋理對(duì)象(其中場景的其余部分是黑色)。
- 來自步驟2的結(jié)果紋理是水平模糊的 - 結(jié)果存儲(chǔ)在紋理中,然后用于垂直模糊。
- 將存儲(chǔ)了垂直和水平模糊圖像的紋理(步驟3的結(jié)果)和來自步驟1的紋理混合(水平和垂直)并繪制到后緩沖器中。
在渲染過程中混合效果不是恒定的:它從非常弱變?yōu)榉浅?qiáng)。 這是通過重復(fù)步驟3不同的次數(shù)(取決于所需的效果強(qiáng)度)來實(shí)現(xiàn)的 - 唯一的區(qū)別是對(duì)于第n次迭代,將生成的第(n-1)個(gè)結(jié)果作為源 對(duì)于水平模糊。 為了使綻放效果更加平滑,我們還使用紋理的連續(xù)采樣。 來自步驟3的最后兩次迭代的結(jié)果用于最終組合傳遞。 這兩個(gè)紋理的顏色與適當(dāng)?shù)囊蜃又祷旌显谝黄稹?/p>
除了綻放效果,該應(yīng)用程序還顯示:
- 矩陣計(jì)算(例如用于透視圖),
- 實(shí)例化繪圖(在屏幕上繪制的每個(gè)立方體是同一對(duì)象的實(shí)例),
- 照明(模型由定向燈照亮),
- 渲染成紋理。
Instanced Drawing
在屏幕上繪制的每個(gè)立方體都是同一對(duì)象的實(shí)例。 讓我們一步一步地完成整個(gè)機(jī)制。
假設(shè)為負(fù)責(zé)場景渲染的活動(dòng)程序?qū)ο笳{(diào)用以下所有函數(shù)。
我們想繪制一個(gè)5x5的二維立方體陣列。 值得一提的是,白色和藍(lán)色立方體仍然是同一物體的實(shí)例。
#define NUMBER_OF_CUBES (25) 復(fù)制代碼生成立方體形狀。 我們希望我們的立方體有點(diǎn)小,以適應(yīng)屏幕。 在這個(gè)簡單的模型中,我們可以通過多種方式實(shí)現(xiàn)這一目標(biāo),但讓我們使用最簡單的模型 - 使用乘數(shù)來縮放立方體。 由于該立方體頂點(diǎn)不會(huì)在<-1,-1,-1>到<1,1,1>的范圍內(nèi)擴(kuò)展,而是根據(jù)使用的乘數(shù)進(jìn)行調(diào)整。
#define CUBE_SCALAR (0.8f) 復(fù)制代碼現(xiàn)在我們需要將生成的信息傳輸?shù)綌?shù)組緩沖區(qū)對(duì)象中。
/* Generate buffer object and fill it with cube vertex coordinates data. */ GL_CHECK(glGenBuffers(1,&sceneRenderingObjects.bufferObjectIdCubeCoords) ); GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER,sceneRenderingObjects.bufferObjectIdCubeCoords) ); GL_CHECK(glBufferData(GL_ARRAY_BUFFER,nOfCubeCoordinates * sizeof(GLfloat),(GLfloat*)cubeCoordinates,GL_STATIC_DRAW) ); 復(fù)制代碼定義通用頂點(diǎn)屬性數(shù)據(jù)的數(shù)組。 確保正確緩沖區(qū)對(duì)象當(dāng)前綁定到GL_ARRAY_BUFFER(包含立方體頂點(diǎn)數(shù)據(jù)的緩沖區(qū)對(duì)象)。 在我們的應(yīng)用程序中,我們多次重新綁定它,因此在調(diào)用glVertexAttribPointer()之前,我們再次綁定它。 在接下來的一個(gè)步驟中,我們將描述sceneRenderingProgramLocationsPtr-> attribCubeVertexCoordinates所代表的內(nèi)容(將在用于實(shí)例化繪圖的著色器對(duì)象中進(jìn)行描述)。
/* Cube coordinates are constant during rendering process. Set them now. */ GL_CHECK(glBindBuffer (GL_ARRAY_BUFFER,sceneRenderingObjects.bufferObjectIdCubeCoords) ); GL_CHECK(glVertexAttribPointer(sceneRenderingProgramLocations.attribCubeVertexCoordinates,NUMBER_OF_COMPONENTS_PER_VERTEX,GL_FLOAT,GL_FALSE,0,NULL) ); 復(fù)制代碼準(zhǔn)備就緒后,我們必須啟用頂點(diǎn)屬性數(shù)組。
GL_CHECK(glEnableVertexAttribArray(sceneRenderingProgramLocations.attribCubeVertexCoordinates) ); 復(fù)制代碼我們現(xiàn)在準(zhǔn)備繪制同一對(duì)象的多個(gè)實(shí)例。 我們使用一個(gè)特殊的函數(shù),即glDrawArraysInstanced()
/* Draw scene. */ GL_CHECK(glDrawArraysInstanced(GL_TRIANGLES,0,nOfCubeCoordinates,NUMBER_OF_CUBES) ); 復(fù)制代碼我們希望立方體以5x5二維陣列排列。 我們必須計(jì)算每個(gè)立方體的位置。 這是在getCubeLocations()函數(shù)中完成的。
static GLfloat* getCubeLocations(GLint numberOfColumns,GLint numberOfRows,GLfloat cubeScalar,GLfloat distanceBetweenCubes,GLint* numberOfCubeLocationCoordinatesPtr) {ASSERT(numberOfCubeLocationCoordinatesPtr != NULL);const float distance = distanceBetweenCubes + 2 * cubeScalar; /* A single cube spreads out from* <-cubeScalar, -cubeScalar, -cubeScalar> to* <cubeScalar, cubeScalar, cubeScalar>,* with <0, 0, 0> representing the center of the cube.* We have to enlarge the requested distance between cubes* (2 * cubeScalar) times. */int index = 0;int numberOfCubeLocationCoordinates = 0;const int numberOfPointCoordinates = 2;GLfloat* result = NULL;const float xStart = -( float(numberOfColumns - 1) / 2.0f * distance);const float yStart = -( float(numberOfRows - 1) / 2.0f * distance);numberOfCubeLocationCoordinates = numberOfPointCoordinates * numberOfColumns * numberOfRows;result = (GLfloat*) malloc(numberOfCubeLocationCoordinates * sizeof(GLfloat) );/* Make sure memory allocation succeeded. */ASSERT(result != NULL);for (int rowIndex = 0; rowIndex < numberOfRows; rowIndex++){for (int columnIndex = 0; columnIndex < numberOfColumns; columnIndex++){result[index++] = xStart + (rowIndex * distance);result[index++] = yStart + (columnIndex * distance);}}*numberOfCubeLocationCoordinatesPtr = numberOfCubeLocationCoordinates;return result; } 復(fù)制代碼然后將結(jié)果傳遞給uniform緩沖區(qū)對(duì)象
/* Generate uniform buffer object and fill it with cube positions data. */ GL_CHECK(glGenBuffers(1,&sceneRenderingObjects.bufferObjectIdElementLocations) ); GL_CHECK(glBindBuffer(GL_UNIFORM_BUFFER,sceneRenderingObjects.bufferObjectIdElementLocations) ); GL_CHECK(glBufferData(GL_UNIFORM_BUFFER,nOfCubeLocations * sizeof (GLfloat),cubeLocations,GL_STATIC_DRAW) ); 復(fù)制代碼然后將其用作程序uniform塊數(shù)據(jù)的源。 在接下來的一個(gè)步驟中,我們將描述sceneRenderingProgramLocationsPtr-> uniformBlockCubeProperties代表什么(它將在用于實(shí)例化繪圖的著色器對(duì)象中描述)。
/* Cube locations are constant during rendering process. Set them now. */ GL_CHECK(glUniformBlockBinding(sceneRenderingProgramShaderObjects.programObjectId,sceneRenderingProgramLocations.uniformBlockCubeProperties,0 ) ); GL_CHECK(glBindBufferBase (GL_UNIFORM_BUFFER,0,sceneRenderingObjects.bufferObjectIdElementLocations) ); 復(fù)制代碼Shader Objects used for Instanced Drawing
頂點(diǎn)著色器源:
#version 300 es precision mediump float; #define NUMBER_OF_CUBES (25) const int is_diagonal_cube[NUMBER_OF_CUBES] = int[NUMBER_OF_CUBES](1, 0, 0, 0, 1,0, 1, 0, 1, 0,0, 0, 1, 0, 0,0, 1, 0, 1, 0,1, 0, 0, 0, 1); /* UNIFORMS */ uniform mat4 mv_matrix; uniform mat4 mvp_matrix; uniform cube_properties {vec2 locations[NUMBER_OF_CUBES]; }; /* ATTRIBUTES */ in vec3 cube_vertex_coordinates; in vec3 cube_vertex_normals; /* OUTPUTS */out vec3 normal;out vec4 vertex; flat out int is_cube_placed_on_diagonal; void main() {/* Prepare translation matrix. */mat4 cube_location_matrix = mat4(1.0, 0.0, 0.0, 0.0,0.0, 1.0, 0.0, 0.0,0.0, 0.0, 1.0, 0.0,locations[gl_InstanceID].x, locations[gl_InstanceID].y, 0.0, 1.0);/* Calculate matrices. */mat4 model_view_matrix = mv_matrix * cube_location_matrix;mat4 model_view_projection_matrix = mvp_matrix * cube_location_matrix;/* Set output values. */is_cube_placed_on_diagonal = is_diagonal_cube[gl_InstanceID];normal = vec3(model_view_matrix * vec4(cube_vertex_normals, 0.0)).xyz;vertex = model_view_matrix * vec4(cube_vertex_coordinates, 1.0);/* Set vertex position in NDC space. */gl_Position = model_view_projection_matrix * vec4(cube_vertex_coordinates, 1.0); } 復(fù)制代碼如所見,在頂點(diǎn)著色器中我們使用了cube_vertex_coordinates屬性。 并且它的位置應(yīng)該用作前面步驟之一中描述的glVertexAttribPointer()中的第一個(gè)參數(shù)。
要獲取屬性位置,只需調(diào)用:
locationsStoragePtr->attribCubeVertexCoordinates = GL_CHECK(glGetAttribLocation(programObjectId, "cube_vertex_coordinates") ); 復(fù)制代碼還使用了uniform塊(cube_properties)。 它的位置應(yīng)該用作前面步驟之一中描述的glUniformBlockBinding()中的第二個(gè)參數(shù)。
要獲得uniform塊位置,只需調(diào)用:
locationsStoragePtr->uniformBlockCubeProperties = GL_CHECK(glGetUniformBlockIndex(programObjectId, "cube_properties") ); 復(fù)制代碼在著色器中,我們有關(guān)于每個(gè)立方體位置的信息。 根據(jù)OpenGL ES著色語言規(guī)范,變量gl_InstanceID是一個(gè)頂點(diǎn)著色器輸入變量,它保存實(shí)例化繪制調(diào)用中當(dāng)前圖元的實(shí)例編號(hào)。 我們使用此變量來引用存儲(chǔ)在uniform塊中的位置數(shù)組的必需元素。 然后將該信息用于準(zhǔn)備平移矩陣,然后將其用于設(shè)置NDC空間中的頂點(diǎn)。
片段著色器源:
#version 300 es precision lowp float; #define EPSILON (0.00001) struct _light_properties {vec3 ambient;vec3 color;float constant_attenuation;float linear_attenuation;vec3 position;float quadratic_attenauation;float shininess;float strength; }; /* UNIFORMS */ uniform vec3 camera_position; uniform _light_properties light_properties; /* INPUTS */in vec3 normal;in vec4 vertex; flat in int is_cube_placed_on_diagonal; /* OUTPUTS */ /* Stores color data of cubes that should not be bloomed.*/ layout(location = 0) out vec4 normal_scene_color; /* Stores color data of cubes we want bloomed. */ layout(location = 1) out vec4 bloom_element_color; void main() {vec4 vertex_color = vec4(0.2, 0.4, 0.8, 1.0); /* Each cube will have the same colour. */vec3 normalized_normals = normalize(normal);vec3 light_direction = normalize(vec3(light_properties.position - vertex.xyz));float attenuation = 1.0 / (light_properties.constant_attenuation + (light_properties.linear_attenuation + light_properties.quadratic_attenauation));vec3 camera_direction = camera_position - vec3(vertex);float diffuse = max(0.0, dot(normalized_normals, light_direction));vec3 half_vector = normalize(light_direction + camera_direction);float specular = 0.0;if (abs(diffuse - 0.0) > EPSILON){specular = max(0.0, dot(half_vector, normal));specular = pow(specular, light_properties.shininess) * light_properties.strength;}vec3 scattered_light = light_properties.ambient * attenuation + diffuse * attenuation * light_properties.color;vec3 reflected_light = light_properties.color * specular * attenuation;vec3 calculated_color = min(vertex_color.xyz * scattered_light + reflected_light, vec3(1.0) );/* Set output variables. */vec4 color_to_be_returned = vec4(calculated_color, 1.0);normal_scene_color = vec4(0.0);bloom_element_color = vec4(0.0);/* If we are dealing with a cube placed on a diagonal, use white colour.* Otherwise, we want to output a regular cube (which means the previously* calculated cube colour with lighting applied). */if (is_cube_placed_on_diagonal == 1){bloom_element_color = vec4(1.0, 1.0, 1.0, 1.0);}else{normal_scene_color = color_to_be_returned;} } 復(fù)制代碼在片段著色器中,實(shí)現(xiàn)了一些用于應(yīng)用光照效果的顏色計(jì)算。
Rendering Scene to Texture
應(yīng)用程序中,我們多次使用渲染到紋理機(jī)制,但讓我們僅使用一個(gè)案例來解釋這一點(diǎn)。
如果我們希望將場景渲染為紋理,我們應(yīng)該執(zhí)行以下操作:
生成幀緩沖區(qū)和紋理對(duì)象。 不要忘記為這些對(duì)象設(shè)置適當(dāng)?shù)膮?shù)。 我們希望只有一個(gè)來自著色器對(duì)象的輸出(一個(gè)帶有藍(lán)色和白色立方體的顏色場景)。 我們也需要深度紋理,因?yàn)閳鼍氨欢ㄏ蚬庹樟?#xff0c;深度值將用于光計(jì)算。
/* Generate objects. */ GL_CHECK(glGenFramebuffers(1,framebufferObjectIdPtr) ); GL_CHECK(glGenTextures (1,originalTextureObjectIdPtr) ); GL_CHECK(glGenTextures (1,depthToIdPtr) ); /* Bind generated framebuffer and texture objects to specific binding points. */ GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER,*framebufferObjectIdPtr) ); GL_CHECK(glBindTexture (GL_TEXTURE_2D,*originalTextureObjectIdPtr) ); GL_CHECK(glTexImage2D (GL_TEXTURE_2D,0,GL_RGBA8,windowWidth,windowHeight,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL) ); GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE) ); GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE) ); GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR) ); GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR) ); GL_CHECK(glBindTexture (GL_TEXTURE_2D,*depthToIdPtr) ); GL_CHECK(glTexImage2D (GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT32F,windowWidth,windowHeight,0,GL_DEPTH_COMPONENT,GL_FLOAT,NULL) ); GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST) ); GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST) ); GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE) ); GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE) ); 復(fù)制代碼下一步是將生成的紋理對(duì)象附加到特定綁定點(diǎn)處的幀緩沖對(duì)象。
/* Bind colour and depth textures to framebuffer object. */ GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,*originalTextureObjectIdPtr,0) ); GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,*depthToIdPtr,0) ); 復(fù)制代碼剩下的只有一件事:實(shí)際渲染。
渲染到紋理對(duì)象是通過將framebuffer對(duì)象綁定到GL_DRAW_FRAMEBUFFER幀緩沖區(qū)綁定點(diǎn)(使用glBindFramebuffer()函數(shù))來實(shí)現(xiàn)的。 只有在綁定了默認(rèn)幀緩沖對(duì)象(ID = 0)時(shí),才能渲染到后緩沖區(qū)(輸出將在屏幕上可見)。
/* Render scene. * The scene is rendered to two render targets: * - 1. First texture will store color data; * - 2. Second texture will store color data, but only for the cubes that should be * affected by the bloom operation (remaining objects will not be rendered). */ GL_CHECK(glUseProgram(sceneRenderingProgramShaderObjects.programObjectId) ); {/* Bind a framebuffer object to the GL_DRAW_FRAMEBUFFER framebuffer binding point,* so that everything we render will end up in the FBO's attachments. */GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER,sceneRenderingObjects.framebufferObjectId) );/* Set the viewport for the whole screen size. */GL_CHECK(glViewport(0,0,windowWidth,windowHeight) );/* Clear the framebuffer's content. */GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) );/* [Instanced drawing] *//* Draw scene. */GL_CHECK(glDrawArraysInstanced(GL_TRIANGLES,0,nOfCubeCoordinates,NUMBER_OF_CUBES) );/* [Instanced drawing] */ } 復(fù)制代碼Rendering Texture on Screen
本節(jié)介紹如何將紋理渲染到屏幕中。 在此示例中,我們使用更高級(jí)的紋理渲染,但讓我們從初級(jí)開始描述問題。
需要做的第一件事是創(chuàng)建一個(gè)附加了著色器的程序?qū)ο?#xff0c;然后將其鏈接和使用。
還需要的是一個(gè)填充數(shù)據(jù)的紋理對(duì)象(例如,可以通過將場景渲染到紋理中描述的步驟來獲取此對(duì)象)。 假設(shè)textureID指的是紋理對(duì)象,textureUnit指的是要用作綁定點(diǎn)的紋理單元。 此紋理必須在特定綁定點(diǎn)綁定到GL_TEXTURE_2D目標(biāo)。
glActiveTexture(GL_TEXTURE0 + textureUnit); /* textureUnit has to be a value from a range [0, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1] */ glBindTexture(GL_TEXTURE_2D, textureID); 復(fù)制代碼如果要使用默認(rèn)紋理單元部件,則可以跳過選擇活動(dòng)紋理單元部件。
頂點(diǎn)著色器源:
#version 300 es precision mediump float; /* GL_TRIANGLE_FAN-type quad vertex data. */ const vec4 vertex_positions[4] = vec4[4](vec4( 1.0, -1.0, 0.0, 1.0),vec4(-1.0, -1.0, 0.0, 1.0),vec4(-1.0, 1.0, 0.0, 1.0),vec4( 1.0, 1.0, 0.0, 1.0) ); /* Texture UVs. */ const vec2 texture_uv[4] = vec2[4](vec2(1.0, 0.0),vec2(0.0, 0.0),vec2(0.0, 1.0),vec2(1.0, 1.0) ); /* OUTPUTS */ out vec2 texture_coordinates; void main() {/* Return vertex coordinates. */gl_Position = vertex_positions[gl_VertexID];/* Pass texture coordinated to fragment shader. */texture_coordinates = texture_uv[gl_VertexID]; } 復(fù)制代碼如所見,我們使用的是GL_TRIANGLE_FAN類型的四倍頂點(diǎn)數(shù)據(jù)。 這很重要,因?yàn)槲覀儽仨毷褂肙penGL ES繪制功能的相應(yīng)模式。
片段著色器源:
#version 300 es precision mediump float; /* UNIFORMS */ uniform sampler2D sample_texture; /* INPUTS */ in vec2 texture_coordinates; /* OUTPUTS */ out vec4 color; void main() {/* Return colour. */color = texture(sample_texture, texture_coordinates); } 復(fù)制代碼假設(shè)programID是創(chuàng)建并鏈接的程序?qū)ο驣D(附加了著色器對(duì)象,如上所述)。
/* Set active program. */ glUseProgram(programID); /* Get texture sample uniform location. */ GLint sampleTextureUniformLocation = glGetUniformLocation(programID, "sample_texture"); /* Set uniform value. */ glUniform1i(sampleTextureUniformLocation, textureUnit); /* Bind default framebuffer object. */ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); /* Draw texture on a screen. */ glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 復(fù)制代碼這對(duì)于使用默認(rèn)幀緩沖對(duì)象很重要(由于渲染結(jié)果將顯示在屏幕上)。 這就是我們調(diào)用glBindFramebuffer()函數(shù)的原因,但默認(rèn)情況下這不是強(qiáng)制性的。 如果不使用任何幀緩沖對(duì)象,則可以跳過此行。
如果使用GL_TEXTURE0,可以跳過設(shè)置統(tǒng)一值的行,因?yàn)槟J(rèn)情況下會(huì)出現(xiàn)這種情況。 如果想使用任何其他綁定點(diǎn),那么這條線是必不可少的,因?yàn)樗赋鰬?yīng)該渲染哪個(gè)紋理對(duì)象。
Prepare Scene That Will Be Bloomed
已經(jīng)描述了如何將場景渲染為紋理。 現(xiàn)在,我們應(yīng)該有一個(gè)包含顏色場景數(shù)據(jù)的紋理,在我們的例子中是5x5陣列,由藍(lán)色和白色立方體組成。
我們此時(shí)想要實(shí)現(xiàn)的是從該圖像中選擇我們想要綻放的所有片段。 我們只想綻放更明亮的立方體。 我們正在使用程序?qū)ο髞韼椭覀冞x擇這些元素。
如果我們使用我們的顏色場景紋理作為此著色器對(duì)象的輸入,我們可以對(duì)其進(jìn)行采樣并檢查每個(gè)片段的亮度。 如果它高于特定值,則將存儲(chǔ)原始片段顏色,否則我們將存儲(chǔ)黑色值。 存儲(chǔ)這些結(jié)果的紋理對(duì)象然后可以用作模糊算法的輸入。
片段著色器源
static const char getLuminanceImageFragmentShaderSource[] = "#version 300 es\n""precision highp float;\n""/* UNIFORMS */\n""uniform sampler2D texture_sampler;\n""/* INPUTS */\n""in vec2 texture_coordinates;\n""/* OUTPUTS */\n""out vec4 scene_color;\n""#define MIN_LUMINANCE (0.9)\n""void main()\n""{\n"" vec4 sample_color = texture(texture_sampler, texture_coordinates);\n"" float luminance = 0.2125 * sample_color.x +\n"" 0.7154 * sample_color.y +\n"" 0.0721 * sample_color.z;\n"" if (luminance > MIN_LUMINANCE)\n"" {\n"" scene_color = sample_color;\n"" }\n"" else\n"" {\n"" scene_color = vec4(0.0);\n"" }\n""}";復(fù)制代碼頂點(diǎn)著色器源與Rendering Texture on Screen中描述的相同。
我們希望隨后綻放的紋理變得更小(因此bloom算法會(huì)更有效)。 這就是為什么我們需要準(zhǔn)備dowscaled紋理存儲(chǔ)和更新視口。
static void generateDownscaledObjects(GLuint* fboIdPtr,GLuint* toIdPtr) {ASSERT(fboIdPtr != NULL);ASSERT(toIdPtr != NULL);/* Generate objects. */GL_CHECK(glGenFramebuffers(1,fboIdPtr) );GL_CHECK(glGenTextures (1,toIdPtr) );/* Set texture parameters. */GL_CHECK(glBindTexture (GL_TEXTURE_2D,*toIdPtr) );GL_CHECK(glTexStorage2D (GL_TEXTURE_2D,1,GL_RGBA8,windowWidth / WINDOW_RESOLUTION_DIVISOR,windowHeight / WINDOW_RESOLUTION_DIVISOR));GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE) );GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE) );GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR) );GL_CHECK(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR) );/* Make framebuffer object active and bind texture object to it. */GL_CHECK(glBindFramebuffer (GL_FRAMEBUFFER,*fboIdPtr) );GL_CHECK(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,*toIdPtr,0) );/* Restore default bindings. */GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER,0) );GL_CHECK(glBindTexture (GL_TEXTURE_2D,0) ); } 復(fù)制代碼 /* Get the luminance image, store it in the downscaled texture. */ GL_CHECK(glUseProgram(getLuminanceImageProgramShaderObjects.programObjectId)); {GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER,getLuminanceImageBloomObjects.framebufferObjectId));/* Set the viewport for the whole screen size. */GL_CHECK(glViewport(0,0,windowWidth / WINDOW_RESOLUTION_DIVISOR,windowHeight / WINDOW_RESOLUTION_DIVISOR) );/* Clear the framebuffer's content. */GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) );/* Draw texture. */GL_CHECK(glDrawArrays(GL_TRIANGLE_FAN, 0, 4) ); } 復(fù)制代碼彩色場景著色器輸出
應(yīng)該綻放的元素
具有白色立方體的縮小紋理將用作下一操作的輸入。
Blurring
模糊效果分兩個(gè)主要步驟應(yīng)用:
- 水平模糊
- 垂直模糊
模糊著色器(水平模糊)的片段著色器源:
static const char blurHorizontalFragmentShaderSource[] = "#version 300 es\n""precision mediump float;\n""/** Defines gaussian weights. */\n""const float gaussian_weights[] = float[] (0.2270270270,\n"" 0.3162162162,\n"" 0.0702702703);\n""/* UNIFORMS */\n""/** Radius of a blur effect to be applied. */\n""uniform float blur_radius;\n""/** Texture sampler on which the effect will be applied. */\n""uniform sampler2D texture_sampler;\n""/* INPUTS */\n""/** Texture coordinates. */\n""in vec2 texture_coordinates;\n""/* OUTPUTS */\n""/** Fragment colour that will be returned. */\n""out vec4 output_color;\n""void main()\n""{\n"" vec4 total_color = vec4(0.0);\n"" float image_resolution = float((textureSize(texture_sampler, 0)).x);\n"" float blur_step = blur_radius / image_resolution;\n"" /* Calculate blurred colour. */\n"" /* Blur a texel on the right. */\n"" total_color = texture(texture_sampler, vec2(texture_coordinates.x + 1.0 * blur_step, texture_coordinates.y)) * gaussian_weights[0] +\n"" texture(texture_sampler, vec2(texture_coordinates.x + 2.0 * blur_step, texture_coordinates.y)) * gaussian_weights[1] +\n"" texture(texture_sampler, vec2(texture_coordinates.x + 3.0 * blur_step, texture_coordinates.y)) * gaussian_weights[2];\n"" /* Blur a texel on the left. */\n"" total_color += texture(texture_sampler, vec2(texture_coordinates.x - 1.0 * blur_step, texture_coordinates.y)) * gaussian_weights[0] +\n"" texture(texture_sampler, vec2(texture_coordinates.x - 2.0 * blur_step, texture_coordinates.y)) * gaussian_weights[1] +\n"" texture(texture_sampler, vec2(texture_coordinates.x - 3.0 * blur_step, texture_coordinates.y)) * gaussian_weights[2];\n"" /* Set the output colour. */\n"" output_color = vec4(total_color.xyz, 1.0);\n""}"; 復(fù)制代碼模糊著色器(垂直模糊)的片段著色器源:
static const char blurVerticalFragmentShaderSource[] = "#version 300 es\n""precision mediump float;\n""/** Defines gaussian weights. */\n""const float gaussian_weights[] = float[] (0.2270270270,\n"" 0.3162162162,\n"" 0.0702702703);\n""/* UNIFORMS */\n""/** Radius of a blur effect to be applied. */\n""uniform float blur_radius;\n""/** Texture sampler on which the effect will be applied. */\n""uniform sampler2D texture_sampler;\n""/* INPUTS */\n""/** Texture coordinates. */\n""in vec2 texture_coordinates;\n""/* OUTPUTS */\n""/** Fragment colour that will be returned. */\n""out vec4 output_color;\n""void main()\n""{\n"" vec4 total_color = vec4(0.0);\n"" float image_resolution = float((textureSize(texture_sampler, 0)).y);\n"" float blur_step = blur_radius / image_resolution;\n"" /* Calculate blurred colour. */\n"" /* Blur a texel to the top. */\n"" total_color = texture(texture_sampler, vec2(texture_coordinates.x, texture_coordinates.y + 1.0 * blur_step)) * gaussian_weights[0] +\n"" texture(texture_sampler, vec2(texture_coordinates.x, texture_coordinates.y + 2.0 * blur_step)) * gaussian_weights[1] +\n"" texture(texture_sampler, vec2(texture_coordinates.x, texture_coordinates.y + 3.0 * blur_step)) * gaussian_weights[2];\n"" /* Blur a texel to the bottom. */\n"" total_color += texture(texture_sampler, vec2(texture_coordinates.x, texture_coordinates.y - 1.0 * blur_step)) * gaussian_weights[0] +\n"" texture(texture_sampler, vec2(texture_coordinates.x, texture_coordinates.y - 2.0 * blur_step)) * gaussian_weights[1] +\n"" texture(texture_sampler, vec2(texture_coordinates.x, texture_coordinates.y - 3.0 * blur_step)) * gaussian_weights[2];\n"" /* Set the output colour. */\n"" output_color = vec4(total_color.xyz, 1.0);\n""}"; 復(fù)制代碼應(yīng)用模糊效果背后的主要思想是,對(duì)于每個(gè)紋素,其顏色在每一側(cè)(左/底和右/上)以特定權(quán)重展開(根據(jù)高斯權(quán)重代表統(tǒng)計(jì)中的正態(tài)分布)。 這是通過進(jìn)一步從內(nèi)核(當(dāng)前模糊的紋理元素)對(duì)其具有的片段顏色的較低影響來完成的。
在第一遍中,我們應(yīng)用了水平模糊。 一旦準(zhǔn)備就緒,我們也會(huì)使用結(jié)果來應(yīng)用垂直模糊。 該過程顯示在架構(gòu)上。
模糊架構(gòu)
模糊效果已準(zhǔn)備就緒,但結(jié)果相當(dāng)薄弱,與概述中顯示的第一個(gè)屏幕相當(dāng)。 為了使其更強(qiáng),我們多次應(yīng)用模糊效果(在渲染過程中模糊操作的數(shù)量正在改變)。 在下面給出的代碼中,可以看到,總模糊效果存儲(chǔ)在兩個(gè)紋理對(duì)象中(最后和最后的結(jié)果,但是一個(gè)迭代存儲(chǔ)在兩個(gè)不同的紋理中)。 這種方法的原因?qū)⒃贐lending中解釋。
這里重要的是水平模糊的源紋理正在改變。 在第一個(gè)操作中,我們使用縮小的紋理,元素應(yīng)該是綻放的(Prepare Scene That Will Be Bloomed中描述的結(jié)果)。 第(n)次操作的源紋理是第(n-1)次操作的模糊結(jié)果。 由于這一點(diǎn),已經(jīng)模糊的紋理再次模糊,因此總效果更強(qiáng)。
/* Apply the blur effect. * The blur effect is applied in two basic steps (note that lower resolution textures are used). * a. First, we blur the downscaled bloom texture horizontally. * b. The result of horizontal blurring is then used for vertical blurring. * The result texture contains image blrured in both directions. * c. To amplify the blur effect, steps (a) and (b) are applied multiple times * (with an exception that we now use the resulting blurred texture from the previous pass * as an input to the horizontal blurring pass). * d. The result of last iteration of applying the total blur effect (which is the result after the vertical blur is applied) * is stored in a separate texture. Thanks to that, we have the last and previous blur result textures, * both of which will be then used for continuous sampling (for the blending pass). * */ /* Bind a framebuffer object to the GL_DRAW_FRAMEBUFFER framebuffer binding point, * so that everything we render will end up in the FBO's attachments. */ GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER,blurringObjects.framebufferObjectId) ); /* Set the lower viewport resolution. It corresponds to size of the texture we will be rendering to. */ GL_CHECK(glViewport(0,0,windowWidth / WINDOW_RESOLUTION_DIVISOR,windowHeight / WINDOW_RESOLUTION_DIVISOR) ); GL_CHECK(glEnable (GL_SCISSOR_TEST) ); /* Apply the blur effect multiple times. */ for (int blurIterationIndex = 0;blurIterationIndex < currentNumberOfIterations;blurIterationIndex++) {/* FIRST PASS - HORIZONTAL BLUR* Take the texture showing cubes which should be bloomed and apply a horizontal blur operation.*/GL_CHECK(glUseProgram(blurringHorizontalProgramShaderObjects.programObjectId) );{/* Attach the texture we want the color data to be rendered to the current draw framebuffer.*/GL_CHECK(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,blurringObjects.textureObjectIdHorizontal,0) );/* In first iteration we have to take the texture which shows the cubes we want blurred.* Later, we have to take the same texture that has already been blurred vertically. */if (blurIterationIndex == 0){GL_CHECK(glUniform1i(blurringHorizontalProgramLocations.uniformTextureSampler,TEXTURE_UNIT_BLOOM_SOURCE_TEXTURE) );}else{GL_CHECK(glUniform1i(blurringHorizontalProgramLocations.uniformTextureSampler,TEXTURE_UNIT_BLURRED_TEXTURE) );}/* Draw texture. */GL_CHECK(glDrawArrays(GL_TRIANGLE_FAN, 0, 4) );} /* FIRST PASS - HORIZONTAL BLUR *//* SECOND PASS - VERTICAL BLUR* Take the result of the previous pass (horizontal blur) and apply a vertical blur to this texture.*/GL_CHECK(glUseProgram(blurringVerticalProgramShaderObjects.programObjectId) );{if (blurIterationIndex == currentNumberOfIterations - 1){/* In case of the last iteration, use a different framebuffer object.* The rendering results will be written to the only color attachment of the fbo. */GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER,strongerBlurObjects.framebufferObjectId) );}else{/* Bind a texture object we want the result data to be stored in.*/GL_CHECK(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,blurringObjects.textureObjectIdVertical,0) );}/* Set uniform values. */GL_CHECK(glUniform1i(blurringVerticalProgramLocations.uniformTextureSampler,TEXTURE_UNIT_HORIZONTAL_BLUR_TEXTURE) ); /* Indicates which texture object content should be blurred. *//* Draw texture. */GL_CHECK(glDrawArrays(GL_TRIANGLE_FAN, 0, 4) );} /* SECOND PASS - VERTICAL BLUR */ } /* for (int blur_iteration_index = 0; i < numberOfIterations; blur_iteration_index++) */ GL_CHECK(glDisable(GL_SCISSOR_TEST)); 復(fù)制代碼請(qǐng)查看下面的架構(gòu),看看我們在此操作后獲得的效果。
模糊操作的效果。 較強(qiáng)的模糊效果對(duì)應(yīng)于較高數(shù)量的模糊迭代。
Blending
由于之前的步驟,我們有:
- 包含顏色場景的紋理對(duì)象 - 顏色場景(Rendering Scene to Texture)
- 應(yīng)用較弱模糊效果的紋理對(duì)象持有綻放對(duì)象(Blurring中描述的第n-1次迭代的結(jié)果)
- 應(yīng)用具有更強(qiáng)模糊效果的綻放對(duì)象的紋理對(duì)象(Blurring中描述的第n次迭代的結(jié)果)
我們希望在此步驟中實(shí)現(xiàn)的是混合所有這些紋理并顯示結(jié)果。
我們的方法是模糊效果強(qiáng)度的平滑變化。 這就是為什么我們需要來自模糊循環(huán)的兩個(gè)結(jié)果(第n和第n - 1)。 我們將使用OpenGL ES著色語言mix()函數(shù)來獲得此效果。 這種方法不是強(qiáng)制性的。 你可以只使用最后一個(gè)模糊循環(huán)的結(jié)果(然后不需要著色器中的mix()操作),但是它會(huì)導(dǎo)致模糊效果強(qiáng)度變化的鋸齒狀結(jié)果,這看起來不如平滑變化那么好。
首先,請(qǐng)查看下面的代碼。
/* Apply blend effect.* Take the original scene texture and blend it with texture that contains the total blurring effect.*/ GL_CHECK(glUseProgram(blendingProgramShaderObjects.programObjectId) ); {/* Bind the default framebuffer object. That indicates that the result is to be drawn to the back buffer. */GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0) );/* Set viewport values so that the rendering will take whole screen space. */GL_CHECK(glViewport(0, 0, windowWidth, windowHeight) );/* Set uniform value. */GL_CHECK(glUniform1f(blendingProgramLocations.uniformMixFactor, mixFactor) ); /* Current mixFactor will be used for mixing two textures color values* (texture with higher and lower blur effect value). *//* Clear framebuffer content. */GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) );/* Draw texture. */GL_CHECK(glDrawArrays(GL_TRIANGLE_FAN, 0, 4) ); } 復(fù)制代碼如所見,我們使用默認(rèn)的幀緩沖對(duì)象,因此效果將顯示在屏幕上而不是存儲(chǔ)在渲染目標(biāo)中。 唯一改變的是混合因素。 這將用作著色器中mix()函數(shù)的參數(shù)。 取決于模糊效果方向(如果它增加或減少),mixFactor值會(huì)相應(yīng)地更改(從0到1)。
/* Mix factor value is calculated for a specific frame (for a specific time).* - The number of blur passes varies from MIN_NUMBER_OF_BLUR_PASSES to MAX_NUMBER_OF_BLUR_PASSES* and to MIN_NUMBER_OF_BLUR_PASSES again which indicates the constant animation of increasing* and decreasing blur effect strength.* - For each frame (time) there is a check done to verify the current number of blur passes.* - Once we get the current number of blur passes, we have to calculate the mix factor:* It is changing from 0 to 1 (if the blur effect is increasing) or from 1 to 0 (if the blur effect is decreasing).* This value is set based on a time which passed from the beginning of current number of blur passes rendering in* compare to the total time requested for changing this number.** The 'rendering frame for a specific time' approach is used to avoid a situation of a different effect for slower and faster devices.*/ /* Increase or decrease mixFactor value (depends on blurEffectDirection). */ timeIntervalIndex = (int)(time / TIME_INTERVAL); nOfIterations = (int) timeIntervalIndex % numberOfBlurPasses; if (nOfIterations >= (numberOfBlurPasses / 2)) {nOfIterations = numberOfBlurPasses - (nOfIterations % numberOfBlurPasses) - 1;blurEffectDirection = BLUR_EFFECT_DECREASE; } mixFactor = (time - ((int)(time / TIME_INTERVAL) * TIME_INTERVAL)) / TIME_INTERVAL; currentNumberOfIterations = MIN_NUMBER_OF_BLUR_PASSES + nOfIterations; if (blurEffectDirection == BLUR_EFFECT_DECREASE) {mixFactor = 1.0f - mixFactor; } if (currentNumberOfIterations != lastNumberOfIterations) {shouldSceneBeUpdated = true; } /* Store current number of iterations for future use. */ lastNumberOfIterations = currentNumberOfIterations; 復(fù)制代碼在片段著色器對(duì)象(下面給出的代碼)中,有三個(gè)紋理源輸入(相應(yīng)于上面列出的那些)。 混合較弱且較強(qiáng)的模糊紋理(應(yīng)用mix()函數(shù)返回線性混合)。 將返回的總顏色只是模糊紋理和原始場景紋理的線性混合的總和。
#version 300 es precision mediump float; /* UNIFORMS */ uniform float mix_factor; uniform sampler2D original_texture; uniform sampler2D stronger_blur_texture; uniform sampler2D weaker_blur_texture; /* INPUTS */ in vec2 texture_coordinates; /* OUTPUTS */ out vec4 color; void main() {vec4 stronger_blur_texture_color = texture(stronger_blur_texture, texture_coordinates);vec4 weaker_blur_texture_color = texture(weaker_blur_texture, texture_coordinates);vec4 mixed_blur_color = mix(weaker_blur_texture_color, stronger_blur_texture_color, mix_factor);vec4 original_color = texture(original_texture, texture_coordinates);/* Return blended colour. */color = original_color + mixed_blur_color; } 復(fù)制代碼Min Max Blending
該應(yīng)用程序演示了在OpenGL ES 3.0中以GL_MIN和GL_MAX模式進(jìn)行混合的行為。
呈現(xiàn)人體頭部磁共振的3D紋理
該應(yīng)用程序演示了在GL_MIN和GL_MAX模式下混合的行為。 它呈現(xiàn)3D紋理,其由從人頭部的磁共振獲得的一系列灰度圖像組成。 圖像在Z軸上一個(gè)接一個(gè)地放置,因此當(dāng)啟用混合時(shí),它們模仿頭部的3D模型。
然后旋轉(zhuǎn)紋理坐標(biāo),以便觀察者可以從不同的角度看模型,每5秒后,混合方程就會(huì)改變。 由于U / V / W坐標(biāo)取自間隔<0.0,1.0>并且它們被夾緊到邊緣,因此可能會(huì)出現(xiàn)特定旋轉(zhuǎn)角度的一些失真。 這就是為什么應(yīng)用程序在原始圖像的后面和前面添加了一些空白層。 現(xiàn)在,如果旋轉(zhuǎn)坐標(biāo)超過間隔,則僅重復(fù)附加邊緣層,從而創(chuàng)建無噪聲背景。
由于圖像包含大量黑色,因此定期最小混合會(huì)導(dǎo)致屏幕上出現(xiàn)黑色方塊。 因此,在片段著色器中應(yīng)用閾值,該閾值防止渲染不夠亮的片段。 此外,對(duì)于這兩種類型的混合,必須修改輸出亮度的對(duì)比度以查看更多細(xì)節(jié)。
要使用自己的輸入圖像,請(qǐng)檢查其格式并調(diào)整最小混合閾值,附加邊緣圖層的亮度和對(duì)比度修改器的值。
Program Object
在應(yīng)用程序中,只使用了一個(gè)程序?qū)ο蟆?它負(fù)責(zé)在屏幕上渲染3D紋理,旋轉(zhuǎn)它并丟棄在使用最小混合選項(xiàng)時(shí)不夠亮的碎片。 生成和使用程序?qū)ο?#xff0c;將著色器附加到程序?qū)ο蟛⒕幾g它們。
頂點(diǎn)著色器代碼
/* Input vertex position. */ in vec4 inputPosition; /* Input U/V/W texture coordinates. */ in vec3 inputUVWCoordinates; /* Constant transformation matrices. */ uniform mat4 cameraMatrix; uniform mat4 projectionMatrix; /* Vector storing rotation coefficients for rotation matrices. */ uniform vec3 rotationVector; /* Number of instances that are going to be drawn. */ uniform int instancesCount; /* Output texture coordinates passed to fragment shader. */ out vec3 uvwCoordinates; void main() {mat4 modelViewProjectionMatrix;/* Matrix rotating texture coordinates around X axis. */mat3 xRotationMatrix = mat3(1.0, 0.0, 0.0,0.0, cos(radians(rotationVector.x)), sin(radians(rotationVector.x)), 0.0, -sin(radians(rotationVector.x)), cos(radians(rotationVector.x)));/* Matrix rotating texture coordinates around Y axis. */mat3 yRotationMatrix = mat3(cos(radians(rotationVector.y)), 0.0, -sin(radians(rotationVector.y)), 0.0, 1.0, 0.0,sin(radians(rotationVector.y)), 0.0, cos(radians(rotationVector.y)));/* Matrix rotating texture coordinates around Z axis. */mat3 zRotationMatrix = mat3( cos(radians(rotationVector.z)), sin(radians(rotationVector.z)), 0.0, -sin(radians(rotationVector.z)), cos(radians(rotationVector.z)), 0.0, 0.0, 0.0, 1.0);/* U/V/W coordinates pointing at appropriate layer depending on gl_InstanceID. */vec3 translatedUVWCoordinates = inputUVWCoordinates - vec3(0.0, 0.0, float(gl_InstanceID) / float(instancesCount - 1));/* * Translate from <0.0, 1.0> interval to <-1.0, 1.0> to rotate texture coordinates around their center. * Otherwise, the rotation would take place around the XYZ axes which would spoil the effect.* The translated coordinates should be translated back to <0.0, 1.0> after all rotations are done.*/translatedUVWCoordinates = translatedUVWCoordinates * 2.0 - vec3(1.0);/* Rotate texture coordinates. */translatedUVWCoordinates = xRotationMatrix * translatedUVWCoordinates;translatedUVWCoordinates = yRotationMatrix * translatedUVWCoordinates;translatedUVWCoordinates = zRotationMatrix * translatedUVWCoordinates;/* Translate back to <0.0, 1.0> interval. */uvwCoordinates = (translatedUVWCoordinates + vec3(1.0)) / 2.0;/* Calculate Model-View-Projection matrix. */modelViewProjectionMatrix = projectionMatrix * cameraMatrix;/* Calculate position of vertex. With each instance squares are drawn closer to the viewer. */gl_Position = modelViewProjectionMatrix * (inputPosition + vec4(0.0, 0.0, float(gl_InstanceID) / float(instancesCount - 1), 0.0)); } 復(fù)制代碼頂點(diǎn)著色器負(fù)責(zé)旋轉(zhuǎn)紋理,在我們的示例中,這意味著基于旋轉(zhuǎn)矢量值更新3D紋理的特定圖層的UVW坐標(biāo)。
片段著色器代碼
precision mediump float; precision mediump int; precision mediump isampler3D; /* Value used to normalize colour values. */ const highp int maxShort = 32768; /* Value used to brighten output colour. */ const float contrastModifier = 3.0;/* Input taken from vertex shader. */ in vec3 uvwCoordinates; /* 3D integer texture sampler. */ uniform isampler3D textureSampler; /* Boolean value indicating current blending equation. */ uniform bool isMinBlending; /* Threshold used for min blending. */ uniform float minBlendingThreshold; /* Output variable. */ out vec4 fragColor; void main() {/* Loaded texture short integer data are in big endian order. Swap the bytes. */ivec4 initialTexture = ivec4(texture(textureSampler, uvwCoordinates).rrr, 1.0);ivec4 swappedBytesTextureTemp = (initialTexture << 8) & ivec4(0xFF00);ivec4 swappedBytesTexture = ((initialTexture >> 8) & ivec4(0x00FF)) | swappedBytesTextureTemp;/* Determine output fragment colour. */fragColor = vec4(swappedBytesTexture) / float(maxShort) * contrastModifier;/* If min blending is set, discard fragments that are not bright enough. */if (isMinBlending && length(fragColor) < minBlendingThreshold){discard;} } 復(fù)制代碼片段著色器負(fù)責(zé)對(duì)3D紋理進(jìn)行采樣,并且在使用最小混合模式時(shí),丟棄不夠亮的片段。
3D Textures
三維紋理由一堆2D紋理表示。 要在OpenGL ES 3.0中創(chuàng)建3D紋理,需要:
一旦完成上述所有步驟,我們就可以使用紋理對(duì)象作為程序?qū)ο?D uniform 采樣器的輸入。 我們只使用一個(gè)采樣器對(duì)象和一個(gè)默認(rèn)紋理單元,因此下面描述的步驟不是必需的,但無論如何我們都會(huì)發(fā)布它們,只是為了展示機(jī)制。
首先,我們正在查詢3D采樣器的位置。
GLint textureSamplerLocation = GL_CHECK(glGetUniformLocation(programID, "textureSampler")); 復(fù)制代碼請(qǐng)注意,第二個(gè)參數(shù)應(yīng)對(duì)應(yīng)著色器中使用的 uniform 名稱。
下一步是檢查檢索到的位置是否有效。 如果返回值為-1,則統(tǒng)一被視為非活動(dòng)狀態(tài),并且將忽略任何設(shè)置 uniform 值的嘗試。
ASSERT(textureSamplerLocation != -1, "Could not find location for uniform: textureSampler"); 復(fù)制代碼一旦我們確定,已找到 uniform,我們就可以設(shè)定它的價(jià)值。 它是通過一些基本步驟實(shí)現(xiàn)的。
設(shè)置活動(dòng)紋理單元(默認(rèn)情況下GL_TEXTURE0處于活動(dòng)狀態(tài),但我們?nèi)匀幌胝{(diào)用它來顯示機(jī)制)。
GL_CHECK(glActiveTexture(GL_TEXTURE0)); 復(fù)制代碼然后,我們需要將紋理對(duì)象綁定到GL_TEXTURE_3D目標(biāo)。
GL_CHECK(glBindTexture(GL_TEXTURE_3D, textureID)); 復(fù)制代碼現(xiàn)在,通過調(diào)用下面顯示的函數(shù),使用textureID命名的紋理對(duì)象將用作程序?qū)ο蟮妮斎搿?/p> GL_CHECK(glUniform1i(textureSamplerLocation, 0)); 復(fù)制代碼
在glUniform1i()調(diào)用中,第二個(gè)參數(shù)對(duì)應(yīng)于GL_TEXTURE0紋理單元中的0。 如果我們想在GL_TEXTURE1紋理單元使用綁定到GL_TEXTURE_3D目標(biāo)的3D紋理對(duì)象,則glUniform1i()調(diào)用中的第二個(gè)參數(shù)應(yīng)該等于1。
要在屏幕上繪制3D紋理,我們使用實(shí)例繪制技術(shù)分別渲染每個(gè)紋理圖層。
/* Draw a single square layer consisting of 6 vertices for textureDepth times. */ GL_CHECK(glDrawArraysInstanced(GL_TRIANGLES, 0, 6, textureDepth)); 復(fù)制代碼Blending
該應(yīng)用程序的主要思想是顯示GL_MIN和GL_MAX混合方程之間的差異。 只是為了澄清:特定的混合方程指定了如何確定像素顏色。 更確切地說:如果像素已經(jīng)確定了顏色(它總是有)并且我們想要為它應(yīng)用新顏色,那么我們可以指定兩種顏色的混合方式:我們可以簡單地添加兩種顏色, 減去它們或我們可以使用兩者的最小值或最大值(這是我們的情況)。
首先,我們需要啟用混合。 如果沒有它,新顏色將取代舊顏色,不會(huì)發(fā)出混合。
/* Enable blending. */ GL_CHECK(glEnable(GL_BLEND)); 復(fù)制代碼然后,我們可以通過調(diào)用來改變混合方程
/* Set new blend equation. */ GL_CHECK(glBlendEquation(GL_MIN)) 復(fù)制代碼或者
/* Set new blend equation. */ GL_CHECK(glBlendEquation(GL_MAX)); 復(fù)制代碼具體混合方程的結(jié)果如下所示。
不同混合方程的結(jié)果:GL_MAX(左側(cè))和GL_MIN(右側(cè))。
總結(jié)
以上是生活随笔為你收集整理的OpenGL ES SDK for Android - 3的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NS3 Tutorial 中文版:第三
- 下一篇: android sina oauth2.