Skip to content
This repository has been archived by the owner on Sep 3, 2022. It is now read-only.

Commit

Permalink
Added Rasterizer API for display color management.
Browse files Browse the repository at this point in the history
Use VisualServer::set_screen_lut to set a 3D texture for color management.
Its X, Y, Z axes correspond to R, G, B channels.

LUT transformation happens in Rasterizer, ensuring that all content blitted
to screen are processed exactly once.

This implements part of godotengine#26826.
  • Loading branch information
toasteater authored and fire committed Oct 24, 2019
1 parent 79dae3a commit fa68b8c
Show file tree
Hide file tree
Showing 16 changed files with 251 additions and 25 deletions.
13 changes: 13 additions & 0 deletions doc/classes/VisualServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3165,6 +3165,19 @@
Sets a boot image. The color defines the background color. If [code]scale[/code] is [code]true[/code], the image will be scaled to fit the screen size. If [code]use_filter[/code] is [code]true[/code], the image will be scaled with linear interpolation. If [code]use_filter[/code] is [code]false[/code], the image will be scaled with nearest-neighbor interpolation.
</description>
</method>
<method name="set_screen_lut">
<return type="void">
</return>
<argument index="0" name="lut" type="Image">
</argument>
<argument index="1" name="h_slices" type="int">
</argument>
<argument index="2" name="v_slices" type="int">
</argument>
<description>
Sets a 3D output LUT. It will be used to transform all screen output. If lut is [code]null[/code] the current LUT will be removed.
</description>
</method>
<method name="set_debug_generate_wireframes">
<return type="void">
</return>
Expand Down
1 change: 1 addition & 0 deletions drivers/dummy/rasterizer_dummy.h
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ class RasterizerDummy : public Rasterizer {
RasterizerScene *get_scene() { return &scene; }

void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) {}
void set_screen_lut(const Ref<Image> &p_lut, int p_h_slices, int p_v_slices) {}

void initialize() {}
void begin_frame(double frame_step) {}
Expand Down
78 changes: 61 additions & 17 deletions drivers/gles2/rasterizer_gles2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ void RasterizerGLES2::initialize() {
storage->initialize();
canvas->initialize();
scene->initialize();
state.lut_shader.init();
}

void RasterizerGLES2::begin_frame(double frame_step) {
Expand Down Expand Up @@ -397,34 +398,73 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c
end_frame(true);
}

void RasterizerGLES2::set_screen_lut(const Ref<Image> &p_lut, int p_h_slices, int p_v_slices) {

if (state.screen_lut.is_valid()) {
storage->free(state.screen_lut);
state.screen_lut = RID();
}

if (p_lut.is_null() || p_lut->empty())
return;

RID texture = storage->texture_create();
storage->texture_allocate(texture, p_lut->get_width(), p_lut->get_height(), 0, p_lut->get_format(), VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER);
storage->texture_set_data(texture, p_lut);

state.screen_lut = texture;
state.lut_texel_count = Vector2(p_lut->get_width(), p_lut->get_height());
state.lut_chunk_count = Vector2(p_h_slices, p_v_slices);
}

void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen) {

ERR_FAIL_COND(storage->frame.current_rt);

RasterizerStorageGLES2::RenderTarget *rt = storage->render_target_owner.getornull(p_render_target);
ERR_FAIL_COND(!rt);

canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true);
if (!state.screen_lut.is_valid()) {
// draw normally
canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true);

canvas->state.canvas_shader.set_custom_shader(0);
canvas->state.canvas_shader.bind();
canvas->state.canvas_shader.set_custom_shader(0);
canvas->state.canvas_shader.bind();

canvas->canvas_begin();
glDisable(GL_BLEND);
glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1);
if (rt->external.fbo != 0) {
glBindTexture(GL_TEXTURE_2D, rt->external.color);
canvas->canvas_begin();
glDisable(GL_BLEND);
glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1);
if (rt->external.fbo != 0) {
glBindTexture(GL_TEXTURE_2D, rt->external.color);
} else {
glBindTexture(GL_TEXTURE_2D, rt->color);
}
canvas->draw_generic_textured_rect(p_screen_rect, Rect2(0, 0, 1, -1));
glBindTexture(GL_TEXTURE_2D, 0);
canvas->canvas_end();
} else {
glBindTexture(GL_TEXTURE_2D, rt->color);
// draw with lut
Size2 win_size = OS::get_singleton()->get_window_size();
glDisable(GL_BLEND);
glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo);
glActiveTexture(GL_TEXTURE0);
if (rt->external.fbo != 0) {
glBindTexture(GL_TEXTURE_2D, rt->external.color);
} else {
glBindTexture(GL_TEXTURE_2D, rt->color);
}
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, storage->texture_owner.get(state.screen_lut)->tex_id);
state.lut_shader.bind();
state.lut_shader.set_uniform(LutTransformShaderGLES2::SCREEN_RECT, Color(p_screen_rect.position.x / win_size.width, 1.0 - (p_screen_rect.position.y + p_screen_rect.size.height) / win_size.height, p_screen_rect.size.width / win_size.width, p_screen_rect.size.height / win_size.height));
state.lut_shader.set_uniform(LutTransformShaderGLES2::TEXEL_COUNT, state.lut_texel_count);
state.lut_shader.set_uniform(LutTransformShaderGLES2::CHUNK_COUNT, state.lut_chunk_count);
storage->bind_quad_array();
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
}

// TODO normals

canvas->draw_generic_textured_rect(p_screen_rect, Rect2(0, 0, 1, -1));

glBindTexture(GL_TEXTURE_2D, 0);
canvas->canvas_end();
}

void RasterizerGLES2::output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) {
Expand Down Expand Up @@ -477,6 +517,10 @@ void RasterizerGLES2::end_frame(bool p_swap_buffers) {
}

void RasterizerGLES2::finalize() {

if (state.screen_lut.is_valid()) {
storage->free(state.screen_lut);
}
}

Rasterizer *RasterizerGLES2::_create_current() {
Expand Down
10 changes: 10 additions & 0 deletions drivers/gles2/rasterizer_gles2.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include "rasterizer_storage_gles2.h"
#include "servers/visual/rasterizer.h"

#include "shaders/lut_transform.glsl.gen.h"

class RasterizerGLES2 : public Rasterizer {

static Rasterizer *_create_current();
Expand All @@ -46,12 +48,20 @@ class RasterizerGLES2 : public Rasterizer {

double time_total;

struct State {
RID screen_lut;
Vector2 lut_texel_count;
Vector2 lut_chunk_count;
LutTransformShaderGLES2 lut_shader;
} state;

public:
virtual RasterizerStorage *get_storage();
virtual RasterizerCanvas *get_canvas();
virtual RasterizerScene *get_scene();

virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true);
virtual void set_screen_lut(const Ref<Image> &p_lut, int p_h_slices, int p_v_slices);

virtual void initialize();
virtual void begin_frame(double frame_step);
Expand Down
1 change: 1 addition & 0 deletions drivers/gles2/shaders/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ if 'GLES2_GLSL' in env['BUILDERS']:
env.GLES2_GLSL('tonemap.glsl');
# env.GLES2_GLSL('particles.glsl');
env.GLES2_GLSL('lens_distorted.glsl');
env.GLES2_GLSL('lut_transform.glsl');
48 changes: 48 additions & 0 deletions drivers/gles2/shaders/lut_transform.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* clang-format off */
[vertex]

attribute highp vec4 vertex_attrib; // attrib:0
/* clang-format on */

attribute vec2 uv_in; // attrib:4

uniform highp vec4 screen_rect;

varying vec2 uv_interp;

void main() {
uv_interp = uv_in;
gl_Position = vec4(screen_rect.xy + vertex_attrib.xy * screen_rect.zw, vertex_attrib.zw);
}

/* clang-format off */
[fragment]

uniform vec2 texel_count;
uniform vec2 chunk_count;

uniform sampler2D source; //texunit:0
uniform sampler2D lut; //texunit:1
/* clang-format on */

varying vec2 uv_interp;

vec2 flatten_3d(vec2 chunk_size, vec2 texel_size, vec2 xy, float z_scaled) {
float row = floor(z_scaled / chunk_count.x);
float col = mod(z_scaled, chunk_count.x);
return chunk_size * vec2(col, row) + (chunk_size - texel_size) * xy + texel_size * 0.5;
}

void main() {
vec4 color = texture2D(source, uv_interp);
vec3 clamped_rgb = clamp(color.rgb, 0.0, 1.0);

vec2 texel_size = 1.0 / texel_count;
vec2 chunk_size = 1.0 / chunk_count;

float z_scaled = clamped_rgb.b * (chunk_count.x * chunk_count.y - 1.0);
vec4 low = texture2D(lut, flatten_3d(chunk_size, texel_size, clamped_rgb.rg, floor(z_scaled)));
vec4 high = texture2D(lut, flatten_3d(chunk_size, texel_size, clamped_rgb.rg, ceil(z_scaled)));

gl_FragColor = vec4(mix(low.rgb, high.rgb, mod(z_scaled, 1.0)), color.a);
}
70 changes: 62 additions & 8 deletions drivers/gles3/rasterizer_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ void RasterizerGLES3::initialize() {
storage->initialize();
canvas->initialize();
scene->initialize();
state.lut_shader.init();
}

void RasterizerGLES3::begin_frame(double frame_step) {
Expand Down Expand Up @@ -333,6 +334,34 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c
end_frame(true);
}

void RasterizerGLES3::set_screen_lut(const Ref<Image> &p_lut, int p_h_slices, int p_v_slices) {

if (state.screen_lut.is_valid()) {
storage->free(state.screen_lut);
state.screen_lut = RID();
}

if (p_lut.is_null() || p_lut->empty())
return;

int slice_w = p_lut->get_width() / p_h_slices;
int slice_h = p_lut->get_height() / p_v_slices;

RID texture = storage->texture_create();
storage->texture_allocate(texture, slice_w, slice_h, p_h_slices * p_v_slices, p_lut->get_format(), VS::TEXTURE_TYPE_3D, VS::TEXTURE_FLAG_FILTER);

for (int i = 0; i < p_v_slices; i++) {
for (int j = 0; j < p_h_slices; j++) {
int x = slice_w * j;
int y = slice_h * i;
storage->texture_set_data(texture, p_lut->get_rect(Rect2(x, y, slice_w, slice_h)), i * p_h_slices + j);
}
}

state.screen_lut = texture;
state.lut_texel_count = Vector3(slice_w, slice_h, p_h_slices * p_v_slices);
}

void RasterizerGLES3::blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen) {

ERR_FAIL_COND(storage->frame.current_rt);
Expand All @@ -341,17 +370,39 @@ void RasterizerGLES3::blit_render_target_to_screen(RID p_render_target, const Re
ERR_FAIL_COND(!rt);

#if 1

Size2 win_size = OS::get_singleton()->get_window_size();
if (rt->external.fbo != 0) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo);

if (!state.screen_lut.is_valid()) {
// no lut, just blit buffer
if (rt->external.fbo != 0) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo);
} else {
glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo);
}
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo);
glBlitFramebuffer(0, 0, rt->width, rt->height, p_screen_rect.position.x, win_size.height - p_screen_rect.position.y - p_screen_rect.size.height, p_screen_rect.position.x + p_screen_rect.size.width, win_size.height - p_screen_rect.position.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
} else {
glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo);
// draw with lut
glDisable(GL_BLEND);
if (rt->external.fbo != 0) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo);
} else {
glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo);
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, rt->color);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_3D, storage->texture_owner.get(state.screen_lut)->tex_id);
state.lut_shader.bind();
state.lut_shader.set_uniform(LutTransformShaderGLES3::SCREEN_RECT, Color(p_screen_rect.position.x / win_size.width, 1.0 - (p_screen_rect.position.y + p_screen_rect.size.height) / win_size.height, p_screen_rect.size.width / win_size.width, p_screen_rect.size.height / win_size.height));
state.lut_shader.set_uniform(LutTransformShaderGLES3::TEXEL_COUNT, state.lut_texel_count);
glBindVertexArray(storage->resources.quadie_array);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo);
glBlitFramebuffer(0, 0, rt->width, rt->height, p_screen_rect.position.x, win_size.height - p_screen_rect.position.y - p_screen_rect.size.height, p_screen_rect.position.x + p_screen_rect.size.width, win_size.height - p_screen_rect.position.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);

#else
canvas->canvas_begin();
glDisable(GL_BLEND);
Expand Down Expand Up @@ -418,6 +469,9 @@ void RasterizerGLES3::end_frame(bool p_swap_buffers) {
}

void RasterizerGLES3::finalize() {
if (state.screen_lut.is_valid()) {
storage->free(state.screen_lut);
}

storage->finalize();
canvas->finalize();
Expand Down
9 changes: 9 additions & 0 deletions drivers/gles3/rasterizer_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include "rasterizer_storage_gles3.h"
#include "servers/visual/rasterizer.h"

#include "shaders/lut_transform.glsl.gen.h"

class RasterizerGLES3 : public Rasterizer {

static Rasterizer *_create_current();
Expand All @@ -46,12 +48,19 @@ class RasterizerGLES3 : public Rasterizer {

double time_total;

struct State {
RID screen_lut;
Vector3 lut_texel_count;
LutTransformShaderGLES3 lut_shader;
} state;

public:
virtual RasterizerStorage *get_storage();
virtual RasterizerCanvas *get_canvas();
virtual RasterizerScene *get_scene();

virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true);
virtual void set_screen_lut(const Ref<Image> &p_lut, int p_h_slices, int p_v_slices);

virtual void initialize();
virtual void begin_frame(double frame_step);
Expand Down
1 change: 1 addition & 0 deletions drivers/gles3/shaders/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ if 'GLES3_GLSL' in env['BUILDERS']:
env.GLES3_GLSL('tonemap.glsl');
env.GLES3_GLSL('particles.glsl');
env.GLES3_GLSL('lens_distorted.glsl');
env.GLES3_GLSL('lut_transform.glsl');
35 changes: 35 additions & 0 deletions drivers/gles3/shaders/lut_transform.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* clang-format off */
[vertex]

layout(location = 0) in highp vec4 vertex_attrib;
/* clang-format on */

layout(location = 4) in vec2 uv_in;

uniform highp vec4 screen_rect;

out vec2 uv_interp;

void main() {
uv_interp = uv_in;
gl_Position = vec4(screen_rect.xy + vertex_attrib.xy * screen_rect.zw, vertex_attrib.zw);
}

/* clang-format off */
[fragment]

uniform vec3 texel_count;

uniform sampler2D source; //texunit:0
uniform sampler3D lut; //texunit:1
/* clang-format on */

in vec2 uv_interp;

layout(location = 0) out vec4 frag_color;

void main() {
vec4 color = texture(source, uv_interp);
vec3 coords = clamp(color.rgb, 0.0, 1.0) * (1.0 - 1.0 / texel_count) + 0.5 / texel_count;
frag_color = texture(lut, coords);
}
Loading

0 comments on commit fa68b8c

Please sign in to comment.