Fix T47639: OpenGL render with smoke and fire incorrect when using

transparency.

The issue is that we are rendering to a 0..1 clamped sRGB buffer with
unpremultiplied alpha, where the correct thing to do would be to render
to an unclamped linear premultiplied alpha buffer. Then we would just
make fire purely emissive without affecting the alpha channel at all,
but that doesn't work here.

So for now, draw fire and smoke separately using different shaders and
blend modes, like it used to before the smoke programs were rewritten
(see rB0372b642).
This commit is contained in:
Kévin Dietrich 2016-08-28 16:50:49 +02:00
parent a5261e06a3
commit 116bab702e
Notes: blender-bot 2023-02-14 08:09:01 +01:00
Referenced by issue #47639, OpenGL render with smoke and fire incorrect when using transparency
5 changed files with 142 additions and 87 deletions

View File

@ -288,6 +288,108 @@ static int create_view_aligned_slices(VolumeSlicer *slicer,
return num_points;
}
static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture *tex_spec,
bool use_fire, const float min[3],
const float ob_sizei[3], const float invsize[3])
{
int invsize_location = GPU_shader_get_uniform(shader, "invsize");
int ob_sizei_location = GPU_shader_get_uniform(shader, "ob_sizei");
int min_location = GPU_shader_get_uniform(shader, "min_location");
int soot_location;
int stepsize_location;
int densityscale_location;
int spec_location, flame_location;
int shadow_location, actcol_location;
if (use_fire) {
spec_location = GPU_shader_get_uniform(shader, "spectrum_texture");
flame_location = GPU_shader_get_uniform(shader, "flame_texture");
}
else {
shadow_location = GPU_shader_get_uniform(shader, "shadow_texture");
actcol_location = GPU_shader_get_uniform(shader, "active_color");
soot_location = GPU_shader_get_uniform(shader, "soot_texture");
stepsize_location = GPU_shader_get_uniform(shader, "step_size");
densityscale_location = GPU_shader_get_uniform(shader, "density_scale");
}
GPU_shader_bind(shader);
if (use_fire) {
GPU_texture_bind(sds->tex_flame, 2);
GPU_shader_uniform_texture(shader, flame_location, sds->tex_flame);
GPU_texture_bind(tex_spec, 3);
GPU_shader_uniform_texture(shader, spec_location, tex_spec);
}
else {
float density_scale = 10.0f;
GPU_shader_uniform_vector(shader, stepsize_location, 1, 1, &sds->dx);
GPU_shader_uniform_vector(shader, densityscale_location, 1, 1, &density_scale);
GPU_texture_bind(sds->tex, 0);
GPU_shader_uniform_texture(shader, soot_location, sds->tex);
GPU_texture_bind(sds->tex_shadow, 1);
GPU_shader_uniform_texture(shader, shadow_location, sds->tex_shadow);
float active_color[3] = { 0.9, 0.9, 0.9 };
if ((sds->active_fields & SM_ACTIVE_COLORS) == 0)
mul_v3_v3(active_color, sds->active_color);
GPU_shader_uniform_vector(shader, actcol_location, 3, 1, active_color);
}
GPU_shader_uniform_vector(shader, min_location, 3, 1, min);
GPU_shader_uniform_vector(shader, ob_sizei_location, 3, 1, ob_sizei);
GPU_shader_uniform_vector(shader, invsize_location, 3, 1, invsize);
}
static void unbind_shader(SmokeDomainSettings *sds, GPUTexture *tex_spec, bool use_fire)
{
GPU_shader_unbind();
GPU_texture_unbind(sds->tex);
if (use_fire) {
GPU_texture_unbind(sds->tex_flame);
GPU_texture_unbind(tex_spec);
GPU_texture_free(tex_spec);
}
else {
GPU_texture_unbind(sds->tex_shadow);
}
}
static void draw_buffer(SmokeDomainSettings *sds, GPUShader *shader, const VolumeSlicer *slicer,
const float ob_sizei[3], const float invsize[3], const int num_points, const bool do_fire)
{
GPUTexture *tex_spec = (do_fire) ? create_flame_spectrum_texture() : NULL;
GLuint vertex_buffer;
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * num_points, &slicer->verts[0][0], GL_STATIC_DRAW);
bind_shader(sds, shader, tex_spec, do_fire, slicer->min, ob_sizei, invsize);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, NULL);
glDrawArrays(GL_TRIANGLES, 0, num_points);
glDisableClientState(GL_VERTEX_ARRAY);
unbind_shader(sds, tex_spec, do_fire);
/* cleanup */
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteBuffers(1, &vertex_buffer);
}
void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
const float min[3], const float max[3],
const float viewnormal[3])
@ -299,14 +401,23 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
const bool use_fire = (sds->active_fields & SM_ACTIVE_FIRE) && sds->tex_flame;
GPUShader *shader = GPU_shader_get_builtin_shader(
(use_fire) ? GPU_SHADER_SMOKE_FIRE : GPU_SHADER_SMOKE);
GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_SMOKE);
if (!shader) {
fprintf(stderr, "Unable to create GLSL smoke shader.\n");
return;
}
GPUShader *fire_shader = NULL;
if (use_fire) {
fire_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SMOKE_FIRE);
if (!fire_shader) {
fprintf(stderr, "Unable to create GLSL fire shader.\n");
return;
}
}
const float ob_sizei[3] = {
1.0f / fabsf(ob->size[0]),
1.0f / fabsf(ob->size[1]),
@ -320,50 +431,6 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
TIMEIT_START(draw);
#endif
/* setup smoke shader */
int soot_location = GPU_shader_get_uniform(shader, "soot_texture");
int spec_location = GPU_shader_get_uniform(shader, "spectrum_texture");
int shadow_location = GPU_shader_get_uniform(shader, "shadow_texture");
int flame_location = GPU_shader_get_uniform(shader, "flame_texture");
int actcol_location = GPU_shader_get_uniform(shader, "active_color");
int stepsize_location = GPU_shader_get_uniform(shader, "step_size");
int densityscale_location = GPU_shader_get_uniform(shader, "density_scale");
int invsize_location = GPU_shader_get_uniform(shader, "invsize");
int ob_sizei_location = GPU_shader_get_uniform(shader, "ob_sizei");
int min_location = GPU_shader_get_uniform(shader, "min_location");
GPU_shader_bind(shader);
GPU_texture_bind(sds->tex, 0);
GPU_shader_uniform_texture(shader, soot_location, sds->tex);
GPU_texture_bind(sds->tex_shadow, 1);
GPU_shader_uniform_texture(shader, shadow_location, sds->tex_shadow);
GPUTexture *tex_spec = NULL;
if (use_fire) {
GPU_texture_bind(sds->tex_flame, 2);
GPU_shader_uniform_texture(shader, flame_location, sds->tex_flame);
tex_spec = create_flame_spectrum_texture();
GPU_texture_bind(tex_spec, 3);
GPU_shader_uniform_texture(shader, spec_location, tex_spec);
}
float active_color[3] = { 0.9, 0.9, 0.9 };
float density_scale = 10.0f;
if ((sds->active_fields & SM_ACTIVE_COLORS) == 0)
mul_v3_v3(active_color, sds->active_color);
GPU_shader_uniform_vector(shader, actcol_location, 3, 1, active_color);
GPU_shader_uniform_vector(shader, stepsize_location, 1, 1, &sds->dx);
GPU_shader_uniform_vector(shader, densityscale_location, 1, 1, &density_scale);
GPU_shader_uniform_vector(shader, min_location, 3, 1, min);
GPU_shader_uniform_vector(shader, ob_sizei_location, 3, 1, ob_sizei);
GPU_shader_uniform_vector(shader, invsize_location, 3, 1, invsize);
/* setup slicing information */
const int max_slices = 256;
@ -387,43 +454,23 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
draw_buffer(sds, shader, &slicer, ob_sizei, invsize, num_points, false);
GLuint vertex_buffer;
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * num_points, &slicer.verts[0][0], GL_STATIC_DRAW);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, NULL);
glDrawArrays(GL_TRIANGLES, 0, num_points);
glDisableClientState(GL_VERTEX_ARRAY);
/* Draw fire separately (T47639). */
if (use_fire) {
glBlendFunc(GL_ONE, GL_ONE);
draw_buffer(sds, fire_shader, &slicer, ob_sizei, invsize, num_points, true);
}
#ifdef DEBUG_DRAW_TIME
printf("Draw Time: %f\n", (float)TIMEIT_VALUE(draw));
TIMEIT_END(draw);
#endif
/* cleanup */
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteBuffers(1, &vertex_buffer);
GPU_texture_unbind(sds->tex);
GPU_texture_unbind(sds->tex_shadow);
if (use_fire) {
GPU_texture_unbind(sds->tex_flame);
GPU_texture_unbind(tex_spec);
GPU_texture_free(tex_spec);
}
MEM_freeN(slicer.verts);
GPU_shader_unbind();
glDepthMask(gl_depth_write);
if (!gl_blend) {

View File

@ -78,6 +78,7 @@ set(SRC
shaders/gpu_shader_vsm_store_frag.glsl
shaders/gpu_shader_vsm_store_vert.glsl
shaders/gpu_shader_fx_depth_resolve.glsl
shaders/gpu_shader_fire_frag.glsl
shaders/gpu_shader_smoke_frag.glsl
shaders/gpu_shader_smoke_vert.glsl
@ -99,6 +100,7 @@ set(SRC
)
data_to_c_simple(shaders/gpu_shader_geometry.glsl SRC)
data_to_c_simple(shaders/gpu_shader_fire_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_smoke_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_smoke_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_material.glsl SRC)

View File

@ -45,6 +45,7 @@
#define MAX_EXT_DEFINE_LENGTH 1024
/* Non-generated shaders */
extern char datatoc_gpu_shader_fire_frag_glsl[];
extern char datatoc_gpu_shader_smoke_vert_glsl[];
extern char datatoc_gpu_shader_smoke_frag_glsl[];
extern char datatoc_gpu_shader_vsm_store_vert_glsl[];
@ -618,8 +619,8 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader)
case GPU_SHADER_SMOKE_FIRE:
if (!GG.shaders.smoke_fire)
GG.shaders.smoke_fire = GPU_shader_create(
datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl,
NULL, NULL, "#define USE_FIRE;\n", 0, 0, 0);
datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_fire_frag_glsl,
NULL, NULL, NULL, 0, 0, 0);
retval = GG.shaders.smoke_fire;
break;
}

View File

@ -0,0 +1,17 @@
varying vec3 coords;
uniform sampler3D flame_texture;
uniform sampler1D spectrum_texture;
void main()
{
float flame = texture3D(flame_texture, coords).r;
vec4 emission = texture1D(spectrum_texture, flame);
vec4 color;
color.rgb = emission.a * emission.rgb;
color.a = emission.a;
gl_FragColor = color;
}

View File

@ -8,11 +8,6 @@ uniform float density_scale;
uniform sampler3D soot_texture;
uniform sampler3D shadow_texture;
#ifdef USE_FIRE
uniform sampler3D flame_texture;
uniform sampler1D spectrum_texture;
#endif
void main()
{
/* compute color and density from volume texture */
@ -37,12 +32,5 @@ void main()
/* premultiply alpha */
vec4 color = vec4(soot_alpha * soot_color, soot_alpha);
#ifdef USE_FIRE
/* fire */
float flame = texture3D(flame_texture, coords).r;
vec4 emission = texture1D(spectrum_texture, flame);
color.rgb += (1 - color.a) * emission.a * emission.rgb;
#endif
gl_FragColor = color;
}