Workbench Simplification Refactor

This patch is (almost) a complete rewrite of workbench engine.
The features remain unchanged but the code quality is greatly improved.
Hair shading is brighter but also more correct.

This also introduce the concept of `DRWShaderLibrary` to make a simple
include system inside the GLSL files.

Differential Revision: https://developer.blender.org/D7060
This commit is contained in:
2020-03-11 17:07:43 +01:00
parent f01bc597a8
commit c476c36e40
69 changed files with 4379 additions and 5280 deletions

View File

@@ -191,10 +191,14 @@ void *BLI_memblock_iterstep(BLI_memblock_iter *iter)
return ptr;
}
/* Direct access. elem is element index inside the chosen chunk. */
/* Direct access. elem is element index inside the chosen chunk.
* Double usage: You can set chunk to 0 and set the absolute elem index.
* The correct chunk will be retrieve. */
void *BLI_memblock_elem_get(BLI_memblock *mblk, int chunk, int elem)
{
BLI_assert(chunk < mblk->chunk_len);
BLI_assert(elem < (mblk->chunk_size / mblk->elem_size));
int elem_per_chunk = mblk->chunk_size / mblk->elem_size;
chunk += elem / elem_per_chunk;
elem = elem % elem_per_chunk;
return (char *)(mblk->chunk_list[chunk]) + mblk->elem_size * elem;
}

View File

@@ -97,19 +97,18 @@ set(SRC
engines/eevee/eevee_subsurface.c
engines/eevee/eevee_temporal_sampling.c
engines/eevee/eevee_volumes.c
engines/workbench/solid_mode.c
engines/workbench/transparent_mode.c
engines/workbench/workbench_data.c
engines/workbench/workbench_deferred.c
engines/workbench/workbench_effect_aa.c
engines/workbench/workbench_effect_antialiasing.c
engines/workbench/workbench_effect_cavity.c
engines/workbench/workbench_effect_dof.c
engines/workbench/workbench_effect_fxaa.c
engines/workbench/workbench_effect_taa.c
engines/workbench/workbench_effect_outline.c
engines/workbench/workbench_engine.c
engines/workbench/workbench_forward.c
engines/workbench/workbench_materials.c
engines/workbench/workbench_opaque.c
engines/workbench/workbench_render.c
engines/workbench/workbench_studiolight.c
engines/workbench/workbench_shader.c
engines/workbench/workbench_shadow.c
engines/workbench/workbench_transparent.c
engines/workbench/workbench_volume.c
engines/external/external_engine.c
engines/gpencil/gpencil_antialiasing.c
@@ -247,28 +246,32 @@ data_to_c_simple(engines/eevee/shaders/volumetric_scatter_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_integration_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_cavity_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_cavity_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_common_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_data_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_deferred_composite_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_deferred_background_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_effect_dof_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_effect_fxaa_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_effect_taa_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_forward_composite_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_forward_depth_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_ghost_resolve_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_object_outline_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_composite_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_curvature_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_prepass_vert.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_data_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_effect_cavity_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_effect_dof_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_effect_outline_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_effect_smaa_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_effect_smaa_vert.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_effect_taa_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_image_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_matcap_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_material_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_merge_infront_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_prepass_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_shadow_vert.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_shadow_geom.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_prepass_hair_vert.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_prepass_vert.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_shader_interface_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_shadow_caps_geom.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_shadow_debug_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_volume_vert.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_shadow_geom.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_shadow_vert.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_transparent_accum_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_transparent_resolve_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_volume_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_volume_vert.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_world_light_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_colormanagement_lib.glsl SRC)

View File

@@ -381,9 +381,6 @@ static void OVERLAY_cache_finish(void *vedata)
DRW_texture_ensure_fullscreen_2d(&dtxl->depth_in_front, GPU_DEPTH24_STENCIL8, 0);
GPU_framebuffer_ensure_config(
&dfbl->default_fb,
{GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)});
GPU_framebuffer_ensure_config(
&dfbl->in_front_fb,
{GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), GPU_ATTACHMENT_TEXTURE(dtxl->color)});

View File

@@ -68,22 +68,17 @@ void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata)
OVERLAY_shader_wireframe();
for (int xray = 0; xray < (is_material_shmode ? 1 : 2); xray++) {
/* Only do stencil test if stencil buffer is written by the render engine. */
DRWState stencil_state = is_material_shmode ? 0 : DRW_STATE_STENCIL_EQUAL;
DRWState state = DRW_STATE_FIRST_VERTEX_CONVENTION | DRW_STATE_WRITE_COLOR |
DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
DRWPass *pass;
uint stencil_mask;
if (xray == 0) {
DRW_PASS_CREATE(psl->wireframe_ps, state | stencil_state | pd->clipping_state);
DRW_PASS_CREATE(psl->wireframe_ps, state | pd->clipping_state);
pass = psl->wireframe_ps;
stencil_mask = 0xFF;
}
else {
DRW_PASS_CREATE(psl->wireframe_xray_ps, state | pd->clipping_state);
pass = psl->wireframe_xray_ps;
stencil_mask = 0x00;
}
for (int use_coloring = 0; use_coloring < 2; use_coloring++) {
@@ -94,17 +89,14 @@ void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0);
DRW_shgroup_uniform_bool_copy(grp, "isObjectColor", is_object_color);
DRW_shgroup_uniform_bool_copy(grp, "isRandomColor", is_random_color);
DRW_shgroup_stencil_mask(grp, stencil_mask);
pd->wires_all_grp[xray][use_coloring] = grp = DRW_shgroup_create(wires_sh, pass);
DRW_shgroup_uniform_float_copy(grp, "wireStepParam", 1.0f);
DRW_shgroup_stencil_mask(grp, stencil_mask);
}
pd->wires_sculpt_grp[xray] = grp = DRW_shgroup_create(wires_sh, pass);
DRW_shgroup_uniform_float_copy(grp, "wireStepParam", 10.0f);
DRW_shgroup_uniform_bool_copy(grp, "useColoring", false);
DRW_shgroup_stencil_mask(grp, stencil_mask);
}
if (is_material_shmode) {

View File

@@ -1,81 +0,0 @@
out vec4 fragColor;
uniform sampler2D depthBuffer;
uniform sampler2D colorBuffer;
uniform sampler2D normalBuffer;
uniform usampler2D objectId;
uniform vec2 invertedViewportSize;
uniform mat4 WinMatrix; /* inverse WinMatrix */
uniform vec4 viewvecs[3];
uniform vec4 ssao_params;
uniform vec4 ssao_settings;
uniform vec2 curvature_settings;
uniform sampler2D ssao_jitter;
layout(std140) uniform samples_block
{
vec4 ssao_samples[500];
};
#define ssao_samples_num ssao_params.x
#define jitter_tilling ssao_params.yz
#define ssao_iteration ssao_params.w
#define ssao_distance ssao_settings.x
#define ssao_factor_cavity ssao_settings.y
#define ssao_factor_edge ssao_settings.z
#define ssao_attenuation ssao_settings.w
vec3 get_view_space_from_depth(in vec2 uvcoords, in float depth)
{
if (WinMatrix[3][3] == 0.0) {
/* Perspective */
float d = 2.0 * depth - 1.0;
float zview = -WinMatrix[3][2] / (d + WinMatrix[2][2]);
return zview * (viewvecs[0].xyz + vec3(uvcoords, 0.0) * viewvecs[1].xyz);
}
else {
/* Orthographic */
vec3 offset = vec3(uvcoords, depth);
return viewvecs[0].xyz + offset * viewvecs[1].xyz;
}
}
/* forward declaration */
void ssao_factors(in float depth,
in vec3 normal,
in vec3 position,
in vec2 screenco,
out float cavities,
out float edges);
void main()
{
vec2 screenco = vec2(gl_FragCoord.xy) * invertedViewportSize;
ivec2 texel = ivec2(gl_FragCoord.xy);
float cavity = 0.0, edges = 0.0, curvature = 0.0;
#ifdef USE_CAVITY
float depth = texelFetch(depthBuffer, texel, 0).x;
vec3 position = get_view_space_from_depth(screenco, depth);
vec3 normal_viewport = workbench_normal_decode(texelFetch(normalBuffer, texel, 0).rg);
ssao_factors(depth, normal_viewport, position, screenco, cavity, edges);
#endif
#ifdef USE_CURVATURE
curvature = calculate_curvature(
objectId, normalBuffer, texel, curvature_settings.x, curvature_settings.y);
#endif
float final_cavity_factor = clamp((1.0 - cavity) * (1.0 + edges) * (1.0 + curvature), 0.0, 4.0);
/* Using UNORM render target so compress the range. */
fragColor = vec4(final_cavity_factor / CAVITY_BUFFER_RANGE);
}

View File

@@ -1,77 +1,87 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
/* from The Alchemy screen-space ambient obscurance algorithm
layout(std140) uniform samples_block
{
vec4 samples_coords[512];
};
uniform sampler2D cavityJitter;
/* From The Alchemy screen-space ambient obscurance algorithm
* http://graphics.cs.williams.edu/papers/AlchemyHPG11/VV11AlchemyAO.pdf */
void ssao_factors(in float depth,
in vec3 normal,
in vec3 position,
in vec2 screenco,
out float cavities,
out float edges)
void cavity_compute(vec2 screenco,
sampler2D depthBuffer,
sampler2D normalBuffer,
out float cavities,
out float edges)
{
cavities = edges = 0.0;
/* early out if there is no need for SSAO */
if (ssao_factor_cavity == 0.0 && ssao_factor_edge == 0.0) {
float depth = texture(depthBuffer, screenco).x;
/* Early out if background and infront. */
if (depth == 1.0 || depth == 0.0) {
return;
}
/* take the normalized ray direction here */
vec3 noise = texture(ssao_jitter, screenco.xy * jitter_tilling).rgb;
vec3 position = view_position_from_depth(screenco, depth, world_data.viewvecs, ProjectionMatrix);
vec3 normal = workbench_normal_decode(texture(normalBuffer, screenco));
vec2 jitter_co = (screenco * world_data.viewport_size.xy) * world_data.cavity_jitter_scale;
vec3 noise = texture(cavityJitter, jitter_co).rgb;
/* find the offset in screen space by multiplying a point
* in camera space at the depth of the point by the projection matrix. */
vec2 offset;
float homcoord = WinMatrix[2][3] * position.z + WinMatrix[3][3];
offset.x = WinMatrix[0][0] * ssao_distance / homcoord;
offset.y = WinMatrix[1][1] * ssao_distance / homcoord;
float homcoord = ProjectionMatrix[2][3] * position.z + ProjectionMatrix[3][3];
offset.x = ProjectionMatrix[0][0] * world_data.cavity_distance / homcoord;
offset.y = ProjectionMatrix[1][1] * world_data.cavity_distance / homcoord;
/* convert from -1.0...1.0 range to 0.0..1.0 for easy use with texture coordinates */
offset *= 0.5;
int num_samples = int(ssao_samples_num);
/* Note. Putting noise usage here to put some ALU after texture fetch. */
vec2 rotX = noise.rg;
vec2 rotY = vec2(-rotX.y, rotX.x);
for (int x = 0; x < num_samples; x++) {
int sample_index = x + (int(ssao_iteration) * num_samples);
if (sample_index > 500) {
continue;
}
/* ssao_samples[x].xy is sample direction (normalized).
* ssao_samples[x].z is sample distance from disk center. */
int sample_start = world_data.cavity_sample_start;
int sample_end = world_data.cavity_sample_end;
for (int i = sample_start; i < sample_end && i < 512; i++) {
/* sample_coord.xy is sample direction (normalized).
* sample_coord.z is sample distance from disk center. */
vec3 sample_coord = samples_coords[i].xyz;
/* Rotate with random direction to get jittered result. */
vec2 dir_jittered = vec2(dot(ssao_samples[sample_index].xy, rotX),
dot(ssao_samples[sample_index].xy, rotY));
dir_jittered.xy *= ssao_samples[sample_index].z + noise.b;
vec2 dir_jittered = vec2(dot(sample_coord.xy, rotX), dot(sample_coord.xy, rotY));
dir_jittered.xy *= sample_coord.z + noise.b;
vec2 uvcoords = screenco.xy + dir_jittered * offset;
if (uvcoords.x > 1.0 || uvcoords.x < 0.0 || uvcoords.y > 1.0 || uvcoords.y < 0.0) {
vec2 uvcoords = screenco + dir_jittered * offset;
/* Out of screen case. */
if (any(greaterThan(abs(uvcoords - 0.5), vec2(0.5)))) {
continue;
}
float depth_new = texture(depthBuffer, uvcoords).r;
/* Sample depth. */
float s_depth = texture(depthBuffer, uvcoords).r;
/* Handle Background case */
bool is_background = (depth_new == 1.0);
bool is_background = (s_depth == 1.0);
/* This trick provide good edge effect even if no neighbor is found. */
vec3 pos_new = get_view_space_from_depth(uvcoords, (is_background) ? depth : depth_new);
s_depth = (is_background) ? depth : s_depth;
vec3 s_pos = view_position_from_depth(
uvcoords, s_depth, world_data.viewvecs, ProjectionMatrix);
if (is_background) {
pos_new.z -= ssao_distance;
s_pos.z -= world_data.cavity_distance;
}
vec3 dir = pos_new - position;
vec3 dir = s_pos - position;
float len = length(dir);
float f_cavities = dot(dir, normal);
float f_edge = -f_cavities;
float f_bias = 0.05 * len + 0.0001;
float attenuation = 1.0 / (len * (1.0 + len * len * ssao_attenuation));
float attenuation = 1.0 / (len * (1.0 + len * len * world_data.cavity_attenuation));
/* use minor bias here to avoid self shadowing */
if (f_cavities > -f_bias) {
@@ -82,11 +92,10 @@ void ssao_factors(in float depth,
edges += f_edge * attenuation;
}
}
cavities /= ssao_samples_num;
edges /= ssao_samples_num;
cavities *= world_data.cavity_sample_count_inv;
edges *= world_data.cavity_sample_count_inv;
/* don't let cavity wash out the surface appearance */
cavities = clamp(cavities * ssao_factor_cavity, 0.0, 1.0);
edges = edges * ssao_factor_edge;
cavities = clamp(cavities * world_data.cavity_valley_factor, 0.0, 1.0);
edges = edges * world_data.cavity_ridge_factor;
}

View File

@@ -1,30 +1,16 @@
#define NO_OBJECT_ID uint(0)
#define EPSILON 0.00001
#define M_PI 3.14159265358979323846
#define CAVITY_BUFFER_RANGE 4.0
/* 4x4 bayer matrix prepared for 8bit UNORM precision error. */
#define P(x) (((x + 0.5) * (1.0 / 16.0) - 0.5) * (1.0 / 255.0))
const vec4 dither_mat4x4[4] = vec4[4](vec4(P(0.0), P(8.0), P(2.0), P(10.0)),
vec4(P(12.0), P(4.0), P(14.0), P(6.0)),
vec4(P(3.0), P(11.0), P(1.0), P(9.0)),
vec4(P(15.0), P(7.0), P(13.0), P(5.0)));
float bayer_dither_noise()
{
ivec2 tx1 = ivec2(gl_FragCoord.xy) % 4;
ivec2 tx2 = ivec2(gl_FragCoord.xy) % 2;
return dither_mat4x4[tx1.x][tx1.y];
}
#ifdef WORKBENCH_ENCODE_NORMALS
# define WB_Normal vec2
/* From http://aras-p.info/texts/CompactNormalStorage.html
* Using Method #4: Spheremap Transform */
vec3 workbench_normal_decode(WB_Normal enc)
vec3 workbench_normal_decode(vec4 enc)
{
vec2 fenc = enc.xy * 4.0 - 2.0;
float f = dot(fenc, fenc);
@@ -37,8 +23,9 @@ vec3 workbench_normal_decode(WB_Normal enc)
/* From http://aras-p.info/texts/CompactNormalStorage.html
* Using Method #4: Spheremap Transform */
WB_Normal workbench_normal_encode(vec3 n)
WB_Normal workbench_normal_encode(bool front_face, vec3 n)
{
n = normalize(front_face ? n : -n);
float p = sqrt(n.z * 8.0 + 8.0);
n.xy = clamp(n.xy / p + 0.5, 0.0, 1.0);
return n.xy;
@@ -47,161 +34,64 @@ WB_Normal workbench_normal_encode(vec3 n)
#else
# define WB_Normal vec3
/* Well just do nothing... */
# define workbench_normal_encode(a) (a)
# define workbench_normal_decode(a) (a)
# define workbench_normal_encode(f, a) (a)
# define workbench_normal_decode(a) (a.xyz)
#endif /* WORKBENCH_ENCODE_NORMALS */
/* Encoding into the alpha of a RGBA8 UNORM texture. */
/* Encoding into the alpha of a RGBA16F texture. (10bit mantissa) */
#define TARGET_BITCOUNT 8u
#define METALLIC_BITS 3u /* Metallic channel is less important. */
#define ROUGHNESS_BITS (TARGET_BITCOUNT - METALLIC_BITS)
#define TOTAL_BITS (METALLIC_BITS + ROUGHNESS_BITS)
/* Encode 2 float into 1 with the desired precision. */
float workbench_float_pair_encode(float v1, float v2)
{
// const uint total_mask = ~(0xFFFFFFFFu << TOTAL_BITS);
// const uint v1_mask = ~(0xFFFFFFFFu << ROUGHNESS_BITS);
// const uint v2_mask = ~(0xFFFFFFFFu << METALLIC_BITS);
/* Same as above because some compiler are dumb af. and think we use mediump int. */
const int total_mask = 0xFF;
const int v1_mask = 0x1F;
const int v2_mask = 0x7;
int iv1 = int(v1 * float(v1_mask));
int iv2 = int(v2 * float(v2_mask)) << int(ROUGHNESS_BITS);
return float(iv1 | iv2) * (1.0 / float(total_mask));
return float(iv1 | iv2);
}
void workbench_float_pair_decode(float data, out float v1, out float v2)
{
// const uint total_mask = ~(0xFFFFFFFFu << TOTAL_BITS);
// const uint v1_mask = ~(0xFFFFFFFFu << ROUGHNESS_BITS);
// const uint v2_mask = ~(0xFFFFFFFFu << METALLIC_BITS);
/* Same as above because some compiler are dumb af. and think we use mediump int. */
const int total_mask = 0xFF;
const int v1_mask = 0x1F;
const int v2_mask = 0x7;
int idata = int(data * float(total_mask));
int idata = int(data);
v1 = float(idata & v1_mask) * (1.0 / float(v1_mask));
v2 = float(idata >> int(ROUGHNESS_BITS)) * (1.0 / float(v2_mask));
}
float calculate_transparent_weight(float z, float alpha)
{
#if 0
/* Eq 10 : Good for surfaces with varying opacity (like particles) */
float a = min(1.0, alpha * 10.0) + 0.01;
float b = -gl_FragCoord.z * 0.95 + 1.0;
float w = a * a * a * 3e2 * b * b * b;
#else
/* Eq 7 put more emphasis on surfaces closer to the view. */
// float w = 10.0 / (1e-5 + pow(abs(z) / 5.0, 2.0) + pow(abs(z) / 200.0, 6.0)); /* Eq 7 */
// float w = 10.0 / (1e-5 + pow(abs(z) / 10.0, 3.0) + pow(abs(z) / 200.0, 6.0)); /* Eq 8 */
// float w = 10.0 / (1e-5 + pow(abs(z) / 200.0, 4.0)); /* Eq 9 */
/* Same as eq 7, but optimized. */
float a = abs(z) / 5.0;
float b = abs(z) / 200.0;
b *= b;
float w = 10.0 / ((1e-5 + a * a) + b * (b * b)); /* Eq 7 */
#endif
return alpha * clamp(w, 1e-2, 3e2);
}
/* Special function only to be used with calculate_transparent_weight(). */
float linear_zdepth(float depth, vec4 viewvecs[3], mat4 proj_mat)
{
if (proj_mat[3][3] == 0.0) {
float d = 2.0 * depth - 1.0;
return -proj_mat[3][2] / (d + proj_mat[2][2]);
}
else {
/* Return depth from near plane. */
return depth * viewvecs[1].z;
}
}
vec3 view_vector_from_screen_uv(vec2 uv, vec4 viewvecs[3], mat4 proj_mat)
{
return (proj_mat[3][3] == 0.0) ? normalize(viewvecs[0].xyz + vec3(uv, 0.0) * viewvecs[1].xyz) :
vec3(0.0, 0.0, 1.0);
}
vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped)
{
/* Quick creation of an orthonormal basis */
float a = 1.0 / (1.0 + I.z);
float b = -I.x * I.y * a;
vec3 b1 = vec3(1.0 - I.x * I.x * a, b, -I.x);
vec3 b2 = vec3(b, 1.0 - I.y * I.y * a, -I.y);
vec2 matcap_uv = vec2(dot(b1, N), dot(b2, N));
if (flipped) {
matcap_uv.x = -matcap_uv.x;
if (proj_mat[3][3] == 0.0) {
return normalize(viewvecs[0].xyz + vec3(uv, 0.0) * viewvecs[1].xyz);
}
return matcap_uv * 0.496 + 0.5;
}
bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map)
{
vec2 tile_pos = floor(co.xy);
if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10)
return false;
float tile = 10.0 * tile_pos.y + tile_pos.x;
if (tile >= textureSize(map, 0).x)
return false;
/* Fetch tile information. */
float tile_layer = texelFetch(map, ivec2(tile, 0), 0).x;
if (tile_layer < 0.0)
return false;
vec4 tile_info = texelFetch(map, ivec2(tile, 1), 0);
co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer);
return true;
}
vec4 workbench_sample_texture(sampler2D image,
vec2 coord,
bool nearest_sampling,
bool premultiplied)
{
vec2 tex_size = vec2(textureSize(image, 0).xy);
/* TODO(fclem) We could do the same with sampler objects.
* But this is a quick workaround instead of messing with the GPUTexture itself. */
vec2 uv = nearest_sampling ? (floor(coord * tex_size) + 0.5) / tex_size : coord;
vec4 color = texture(image, uv);
/* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */
if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) {
color.rgb = color.rgb / color.a;
else {
return vec3(0.0, 0.0, 1.0);
}
return color;
}
vec4 workbench_sample_texture_array(sampler2DArray tile_array,
sampler1DArray tile_data,
vec2 coord,
bool nearest_sampling,
bool premultiplied)
vec3 view_position_from_depth(vec2 uvcoords, float depth, vec4 viewvecs[3], mat4 proj_mat)
{
vec2 tex_size = vec2(textureSize(tile_array, 0).xy);
if (proj_mat[3][3] == 0.0) {
/* Perspective */
float d = 2.0 * depth - 1.0;
vec3 uv = vec3(coord, 0);
if (!node_tex_tile_lookup(uv, tile_array, tile_data))
return vec4(1.0, 0.0, 1.0, 1.0);
float zview = -proj_mat[3][2] / (d + proj_mat[2][2]);
/* TODO(fclem) We could do the same with sampler objects.
* But this is a quick workaround instead of messing with the GPUTexture itself. */
uv.xy = nearest_sampling ? (floor(uv.xy * tex_size) + 0.5) / tex_size : uv.xy;
vec4 color = texture(tile_array, uv);
/* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */
if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) {
color.rgb = color.rgb / color.a;
return zview * (viewvecs[0].xyz + vec3(uvcoords, 0.0) * viewvecs[1].xyz);
}
else {
/* Orthographic */
vec3 offset = vec3(uvcoords, depth);
return color;
return viewvecs[0].xyz + offset * viewvecs[1].xyz;
}
}

View File

@@ -0,0 +1,44 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_matcap_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_world_light_lib.glsl)
uniform sampler2D materialBuffer;
uniform sampler2D normalBuffer;
in vec4 uvcoordsvar;
out vec4 fragColor;
void main()
{
/* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */
vec3 I = view_vector_from_screen_uv(uvcoordsvar.st, world_data.viewvecs, ProjectionMatrix);
vec3 N = workbench_normal_decode(texture(normalBuffer, uvcoordsvar.st));
vec4 mat_data = texture(materialBuffer, uvcoordsvar.st);
vec3 base_color = mat_data.rgb;
float roughness, metallic;
workbench_float_pair_decode(mat_data.a, roughness, metallic);
#ifdef V3D_LIGHTING_MATCAP
/* When using matcaps, mat_data.a is the backface sign. */
N = (mat_data.a > 0.0) ? N : -N;
fragColor.rgb = get_matcap_lighting(base_color, N, I);
#endif
#ifdef V3D_LIGHTING_STUDIO
fragColor.rgb = get_world_lighting(base_color, roughness, metallic, N, I);
#endif
#ifdef V3D_LIGHTING_FLAT
fragColor.rgb = base_color;
#endif
fragColor.rgb *= get_shadow(N);
fragColor.a = 1.0;
}

View File

@@ -1,6 +1,5 @@
#ifndef CURVATURE_OFFSET
# define CURVATURE_OFFSET 1
#endif
#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
float curvature_soft_clamp(float curvature, float control)
{
@@ -10,33 +9,35 @@ float curvature_soft_clamp(float curvature, float control)
return 0.25 / control;
}
float calculate_curvature(
usampler2D objectId, sampler2D normalBuffer, ivec2 texel, float ridge, float valley)
void curvature_compute(vec2 uv,
usampler2D objectIdBuffer,
sampler2D normalBuffer,
out float curvature)
{
uint object_up = texelFetchOffset(objectId, texel, 0, ivec2(0, CURVATURE_OFFSET)).r;
uint object_down = texelFetchOffset(objectId, texel, 0, ivec2(0, -CURVATURE_OFFSET)).r;
uint object_left = texelFetchOffset(objectId, texel, 0, ivec2(-CURVATURE_OFFSET, 0)).r;
uint object_right = texelFetchOffset(objectId, texel, 0, ivec2(CURVATURE_OFFSET, 0)).r;
curvature = 0.0;
vec3 offset = vec3(world_data.viewport_size_inv, 0.0) * world_data.ui_scale;
uint object_up = texture(objectIdBuffer, uv + offset.zy).r;
uint object_down = texture(objectIdBuffer, uv - offset.zy).r;
uint object_right = texture(objectIdBuffer, uv + offset.xz).r;
uint object_left = texture(objectIdBuffer, uv - offset.xz).r;
/* Remove object outlines. */
if ((object_up != object_down) || (object_right != object_left)) {
return 0.0;
return;
}
vec2 normal_up = texelFetchOffset(normalBuffer, texel, 0, ivec2(0, CURVATURE_OFFSET)).rg;
vec2 normal_down = texelFetchOffset(normalBuffer, texel, 0, ivec2(0, -CURVATURE_OFFSET)).rg;
vec2 normal_left = texelFetchOffset(normalBuffer, texel, 0, ivec2(-CURVATURE_OFFSET, 0)).rg;
vec2 normal_right = texelFetchOffset(normalBuffer, texel, 0, ivec2(CURVATURE_OFFSET, 0)).rg;
float normal_up = workbench_normal_decode(texture(normalBuffer, uv + offset.zy)).g;
float normal_down = workbench_normal_decode(texture(normalBuffer, uv - offset.zy)).g;
float normal_right = workbench_normal_decode(texture(normalBuffer, uv + offset.xz)).r;
float normal_left = workbench_normal_decode(texture(normalBuffer, uv - offset.xz)).r;
normal_up = workbench_normal_decode(normal_up).rg;
normal_down = workbench_normal_decode(normal_down).rg;
normal_left = workbench_normal_decode(normal_left).rg;
normal_right = workbench_normal_decode(normal_right).rg;
float normal_diff = ((normal_up.g - normal_down.g) + (normal_right.r - normal_left.r));
float normal_diff = (normal_up - normal_down) + (normal_right - normal_left);
if (normal_diff < 0) {
return -2.0 * curvature_soft_clamp(-normal_diff, valley);
curvature = -2.0 * curvature_soft_clamp(-normal_diff, world_data.curvature_valley);
}
else {
curvature = 2.0 * curvature_soft_clamp(normal_diff, world_data.curvature_ridge);
}
return 2.0 * curvature_soft_clamp(normal_diff, ridge);
}

View File

@@ -5,12 +5,42 @@ struct LightData {
};
struct WorldData {
vec4 viewvecs[3];
vec4 viewport_size;
vec4 object_outline_color;
vec4 shadow_direction_vs;
float shadow_focus;
float shadow_shift;
float shadow_mul;
float shadow_add;
/* - 16 bytes alignment- */
LightData lights[4];
vec4 ambient_color;
int num_lights;
int matcap_orientation;
int cavity_sample_start;
int cavity_sample_end;
float cavity_sample_count_inv;
float cavity_jitter_scale;
float cavity_valley_factor;
float cavity_ridge_factor;
float cavity_attenuation;
float cavity_distance;
float curvature_ridge;
float curvature_valley;
float ui_scale;
float _pad0;
int matcap_orientation;
bool use_specular;
int _pad1;
int _pad2;
};
#define viewport_size_inv viewport_size.zw
layout(std140) uniform world_block
{
WorldData world_data;
};

View File

@@ -1,30 +0,0 @@
uniform usampler2D objectId;
uniform vec2 invertedViewportSize;
out vec4 fragColor;
layout(std140) uniform world_block
{
WorldData world_data;
};
void main()
{
vec2 uv_viewport = gl_FragCoord.xy * invertedViewportSize;
#ifndef V3D_SHADING_OBJECT_OUTLINE
fragColor = vec4(0.0);
#else /* !V3D_SHADING_OBJECT_OUTLINE */
ivec2 texel = ivec2(gl_FragCoord.xy);
uint object_id = texelFetch(objectId, texel, 0).r;
float object_outline = calculate_object_outline(objectId, texel, object_id);
fragColor = vec4(world_data.object_outline_color.rgb, 1.0) * (1.0 - object_outline);
#endif /* !V3D_SHADING_OBJECT_OUTLINE */
}

View File

@@ -1,103 +0,0 @@
out vec4 fragColor;
uniform usampler2D objectId;
uniform sampler2D materialBuffer;
uniform sampler2D normalBuffer;
/* normalBuffer contains viewport normals */
uniform sampler2D cavityBuffer;
uniform sampler2D matcapDiffuseImage;
uniform sampler2D matcapSpecularImage;
uniform vec2 invertedViewportSize;
uniform vec4 viewvecs[3];
uniform float shadowMultiplier;
uniform float lightMultiplier;
uniform float shadowShift = 0.1;
uniform float shadowFocus = 1.0;
uniform vec3 materialSingleColor;
layout(std140) uniform world_block
{
WorldData world_data;
};
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
vec2 uv_viewport = gl_FragCoord.xy * invertedViewportSize;
float roughness, metallic;
vec3 base_color;
#ifndef MATDATA_PASS_ENABLED
base_color = materialSingleColor;
metallic = 0.0;
roughness = 0.5;
#else
vec4 material_data = texelFetch(materialBuffer, texel, 0);
base_color = material_data.rgb;
workbench_float_pair_decode(material_data.a, roughness, metallic);
#endif
/* Do we need normals */
#ifdef NORMAL_VIEWPORT_PASS_ENABLED
vec3 normal_viewport = workbench_normal_decode(texelFetch(normalBuffer, texel, 0).rg);
#endif
vec3 I_vs = view_vector_from_screen_uv(uv_viewport, viewvecs, ProjectionMatrix);
/* -------- SHADING --------- */
#ifdef V3D_LIGHTING_FLAT
vec3 shaded_color = base_color;
#elif defined(V3D_LIGHTING_MATCAP)
/* When using matcaps, the metallic is the backface sign. */
normal_viewport = (metallic > 0.0) ? normal_viewport : -normal_viewport;
bool flipped = world_data.matcap_orientation != 0;
vec2 matcap_uv = matcap_uv_compute(I_vs, normal_viewport, flipped);
vec3 matcap_diffuse = textureLod(matcapDiffuseImage, matcap_uv, 0.0).rgb;
# ifdef V3D_SHADING_SPECULAR_HIGHLIGHT
vec3 matcap_specular = textureLod(matcapSpecularImage, matcap_uv, 0.0).rgb;
# else
vec3 matcap_specular = vec3(0.0);
# endif
vec3 shaded_color = matcap_diffuse * base_color + matcap_specular;
#elif defined(V3D_LIGHTING_STUDIO)
# ifdef V3D_SHADING_SPECULAR_HIGHLIGHT
vec3 specular_color = mix(vec3(0.05), base_color, metallic);
vec3 diffuse_color = mix(base_color, vec3(0.0), metallic);
# else
roughness = 0.0;
vec3 specular_color = vec3(0.0);
vec3 diffuse_color = base_color;
# endif
vec3 shaded_color = get_world_lighting(
world_data, diffuse_color, specular_color, roughness, normal_viewport, I_vs);
#endif
/* -------- POST EFFECTS --------- */
#ifdef WB_CAVITY
/* Using UNORM texture so decompress the range */
shaded_color *= texelFetch(cavityBuffer, texel, 0).r * CAVITY_BUFFER_RANGE;
#endif
#ifdef V3D_SHADING_SHADOW
float light_factor = -dot(normal_viewport, world_data.shadow_direction_vs.xyz);
float shadow_mix = smoothstep(shadowFocus, shadowShift, light_factor);
shaded_color *= mix(lightMultiplier, shadowMultiplier, shadow_mix);
#endif
#ifdef V3D_SHADING_OBJECT_OUTLINE
uint object_id = texelFetch(objectId, texel, 0).r;
float object_outline = calculate_object_outline(objectId, texel, object_id);
shaded_color = mix(world_data.object_outline_color.rgb, shaded_color, object_outline);
#endif
fragColor = vec4(shaded_color, 1.0);
}

View File

@@ -0,0 +1,31 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_cavity_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_curvature_lib.glsl)
uniform sampler2D depthBuffer;
uniform sampler2D normalBuffer;
uniform usampler2D objectIdBuffer;
in vec4 uvcoordsvar;
out vec4 fragColor;
void main()
{
float cavity = 0.0, edges = 0.0, curvature = 0.0;
#ifdef USE_CAVITY
cavity_compute(uvcoordsvar.st, depthBuffer, normalBuffer, cavity, edges);
#endif
#ifdef USE_CURVATURE
curvature_compute(uvcoordsvar.st, objectIdBuffer, normalBuffer, curvature);
#endif
float final_cavity_factor = clamp((1.0 - cavity) * (1.0 + edges) * (1.0 + curvature), 0.0, 4.0);
fragColor.rgb = vec3(final_cavity_factor);
fragColor.a = 1.0;
}

View File

@@ -1,14 +0,0 @@
in vec4 uvcoordsvar;
out vec4 FragColor;
uniform sampler2D colorBuffer;
uniform vec2 invertedViewportSize;
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
FragColor = FxaaPixelShader(
uvcoordsvar.st, colorBuffer, invertedViewportSize, 1.0, 0.166, 0.0833);
}

View File

@@ -0,0 +1,24 @@
#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
uniform usampler2D objectIdBuffer;
in vec4 uvcoordsvar;
out vec4 fragColor;
void main()
{
vec3 offset = vec3(world_data.viewport_size_inv, 0.0) * world_data.ui_scale;
vec2 uv = uvcoordsvar.st;
uint center_id = texture(objectIdBuffer, uv).r;
uvec4 adjacent_ids = uvec4(texture(objectIdBuffer, uv + offset.zy).r,
texture(objectIdBuffer, uv - offset.zy).r,
texture(objectIdBuffer, uv + offset.xz).r,
texture(objectIdBuffer, uv - offset.xz).r);
float outline_opacity = 1.0 - dot(vec4(equal(uvec4(center_id), adjacent_ids)), vec4(0.25));
fragColor = world_data.object_outline_color * outline_opacity;
}

View File

@@ -0,0 +1,44 @@
uniform sampler2D edgesTex;
uniform sampler2D areaTex;
uniform sampler2D searchTex;
uniform sampler2D blendTex;
uniform sampler2D colorTex;
uniform float mixFactor;
uniform float taaSampleCountInv;
in vec2 uvs;
in vec2 pixcoord;
in vec4 offset[3];
#if SMAA_STAGE == 0
out vec2 fragColor;
#else
out vec4 fragColor;
#endif
void main()
{
#if SMAA_STAGE == 0
/* Detect edges in color and revealage buffer. */
fragColor = SMAALumaEdgeDetectionPS(uvs, offset, colorTex);
/* Discard if there is no edge. */
if (dot(fragColor, float2(1.0, 1.0)) == 0.0) {
discard;
}
#elif SMAA_STAGE == 1
fragColor = SMAABlendingWeightCalculationPS(
uvs, pixcoord, offset, edgesTex, areaTex, searchTex, vec4(0));
#elif SMAA_STAGE == 2
fragColor = vec4(0.0);
if (mixFactor > 0.0) {
fragColor += SMAANeighborhoodBlendingPS(uvs, offset[0], colorTex, blendTex) * mixFactor;
}
if (mixFactor < 1.0) {
fragColor += texture(colorTex, uvs) * (1.0 - mixFactor);
}
fragColor *= taaSampleCountInv;
#endif
}

View File

@@ -0,0 +1,21 @@
out vec2 uvs;
out vec2 pixcoord;
out vec4 offset[3];
void main()
{
int v = gl_VertexID % 3;
float x = -1.0 + float((v & 1) << 2);
float y = -1.0 + float((v & 2) << 1);
gl_Position = vec4(x, y, 1.0, 1.0);
uvs = (gl_Position.xy + 1.0) * 0.5;
#if SMAA_STAGE == 0
SMAAEdgeDetectionVS(uvs, offset);
#elif SMAA_STAGE == 1
SMAABlendingWeightCalculationVS(uvs, pixcoord, offset);
#elif SMAA_STAGE == 2
SMAANeighborhoodBlendingVS(uvs, offset[0]);
#endif
}

View File

@@ -1,14 +1,11 @@
uniform sampler2D historyBuffer;
uniform sampler2D colorBuffer;
out vec4 colorOutput;
in vec4 uvcoordsvar;
uniform float mixFactor;
out vec4 fragColor;
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
vec4 color_buffer = texelFetch(colorBuffer, texel, 0);
vec4 history_buffer = texelFetch(historyBuffer, texel, 0);
colorOutput = mix(history_buffer, color_buffer, mixFactor);
fragColor = texture(colorBuffer, uvcoordsvar.st);
}

View File

@@ -1,36 +0,0 @@
out vec4 fragColor;
uniform usampler2D objectId;
uniform sampler2D transparentAccum;
uniform sampler2D transparentRevealage;
uniform vec2 invertedViewportSize;
#ifndef ALPHA_COMPOSITE
layout(std140) uniform world_block
{
WorldData world_data;
};
#endif
/* TODO: Bypass the whole shader if there is no xray pass and no outline pass. */
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
vec2 uv_viewport = gl_FragCoord.xy * invertedViewportSize;
/* Listing 4 */
vec4 trans_accum = texelFetch(transparentAccum, texel, 0);
float trans_revealage = trans_accum.a;
trans_accum.a = texelFetch(transparentRevealage, texel, 0).r;
vec3 trans_color = trans_accum.rgb / clamp(trans_accum.a, 1e-4, 5e4);
fragColor.a = 1.0 - trans_revealage;
fragColor.rgb = trans_color * fragColor.a;
#ifdef V3D_SHADING_OBJECT_OUTLINE
uint object_id = texelFetch(objectId, texel, 0).r;
float outline = calculate_object_outline(objectId, texel, object_id);
fragColor = mix(vec4(world_data.object_outline_color.rgb, 1.0), fragColor, outline);
#endif
}

View File

@@ -1,20 +0,0 @@
layout(location = 0) out uint objectId;
uniform float ImageTransparencyCutoff = 0.1;
#ifdef V3D_SHADING_TEXTURE_COLOR
uniform sampler2D image;
in vec2 uv_interp;
#endif
void main()
{
#ifdef V3D_SHADING_TEXTURE_COLOR
if (texture(image, uv_interp).a < ImageTransparencyCutoff) {
discard;
}
#endif
objectId = uint(resource_id + 1) & 0xFFu;
}

View File

@@ -1,118 +0,0 @@
uniform float ImageTransparencyCutoff = 0.1;
#ifdef TEXTURE_IMAGE_ARRAY
uniform sampler2DArray image_tile_array;
uniform sampler1DArray image_tile_data;
#else
uniform sampler2D image;
#endif
uniform bool imageNearest;
uniform bool imagePremultiplied;
uniform float alpha = 0.5;
uniform vec2 invertedViewportSize;
uniform vec4 viewvecs[3];
uniform vec4 materialColorAndMetal;
uniform float materialRoughness;
uniform float shadowMultiplier = 0.5;
uniform float lightMultiplier = 1.0;
uniform float shadowShift = 0.1;
uniform float shadowFocus = 1.0;
#ifdef NORMAL_VIEWPORT_PASS_ENABLED
in vec3 normal_viewport;
#endif /* NORMAL_VIEWPORT_PASS_ENABLED */
#ifdef V3D_SHADING_TEXTURE_COLOR
in vec2 uv_interp;
#endif
#ifdef V3D_SHADING_VERTEX_COLOR
in vec3 vertexColor;
#endif
#ifdef V3D_LIGHTING_MATCAP
uniform sampler2D matcapDiffuseImage;
uniform sampler2D matcapSpecularImage;
#endif
layout(std140) uniform world_block
{
WorldData world_data;
};
layout(location = 0) out vec4 transparentAccum;
layout(location = 1) out
float revealageAccum; /* revealage actually stored in transparentAccum.a */
void main()
{
vec4 base_color;
#if defined(V3D_SHADING_TEXTURE_COLOR)
# ifdef TEXTURE_IMAGE_ARRAY
base_color = workbench_sample_texture_array(
image_tile_array, image_tile_data, uv_interp, imageNearest, imagePremultiplied);
# else
base_color = workbench_sample_texture(image, uv_interp, imageNearest, imagePremultiplied);
# endif
if (base_color.a < ImageTransparencyCutoff) {
discard;
}
#elif defined(V3D_SHADING_VERTEX_COLOR)
base_color.rgb = vertexColor;
#else
base_color.rgb = materialColorAndMetal.rgb;
#endif /* V3D_SHADING_TEXTURE_COLOR */
vec2 uv_viewport = gl_FragCoord.xy * invertedViewportSize;
vec3 I_vs = view_vector_from_screen_uv(uv_viewport, viewvecs, ProjectionMatrix);
#ifdef NORMAL_VIEWPORT_PASS_ENABLED
vec3 nor = normalize(normal_viewport);
#endif
/* -------- SHADING --------- */
#ifdef V3D_LIGHTING_FLAT
vec3 shaded_color = base_color.rgb;
#elif defined(V3D_LIGHTING_MATCAP)
bool flipped = world_data.matcap_orientation != 0;
vec2 matcap_uv = matcap_uv_compute(I_vs, nor, flipped);
vec3 matcap_diffuse = textureLod(matcapDiffuseImage, matcap_uv, 0.0).rgb;
# ifdef V3D_SHADING_SPECULAR_HIGHLIGHT
vec3 matcap_specular = textureLod(matcapSpecularImage, matcap_uv, 0.0).rgb;
# else
vec3 matcap_specular = vec3(0.0);
# endif
vec3 shaded_color = matcap_diffuse * base_color.rgb + matcap_specular;
#elif defined(V3D_LIGHTING_STUDIO)
# ifdef V3D_SHADING_SPECULAR_HIGHLIGHT
float metallic = materialColorAndMetal.a;
vec3 specular_color = mix(vec3(0.05), base_color.rgb, metallic);
vec3 diffuse_color = mix(base_color.rgb, vec3(0.0), metallic);
# else
vec3 specular_color = vec3(0.0);
vec3 diffuse_color = base_color.rgb;
# endif
vec3 shaded_color = get_world_lighting(
world_data, diffuse_color, specular_color, materialRoughness, nor, I_vs);
#endif
#ifdef V3D_SHADING_SHADOW
float light_factor = -dot(nor, world_data.shadow_direction_vs.xyz);
float shadow_mix = smoothstep(shadowFocus, shadowShift, light_factor);
shaded_color *= mix(lightMultiplier, shadowMultiplier, shadow_mix);
#endif
/* Based on :
* McGuire and Bavoil, Weighted Blended Order-Independent Transparency, Journal of
* Computer Graphics Techniques (JCGT), vol. 2, no. 2, 122141, 2013
*/
/* Listing 4 */
float z = linear_zdepth(gl_FragCoord.z, viewvecs, ProjectionMatrix);
float weight = calculate_transparent_weight(z, alpha);
transparentAccum = vec4(shaded_color * weight, alpha);
revealageAccum = weight;
}

View File

@@ -1,13 +0,0 @@
uniform sampler2D depthBuffer;
void main(void)
{
float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r;
/* background, discard */
if (depth >= 1.0) {
discard;
}
gl_FragDepth = depth;
}

View File

@@ -0,0 +1,83 @@
/* TODO(fclem) deduplicate code. */
bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map)
{
vec2 tile_pos = floor(co.xy);
if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10)
return false;
float tile = 10.0 * tile_pos.y + tile_pos.x;
if (tile >= textureSize(map, 0).x)
return false;
/* Fetch tile information. */
float tile_layer = texelFetch(map, ivec2(tile, 0), 0).x;
if (tile_layer < 0.0)
return false;
vec4 tile_info = texelFetch(map, ivec2(tile, 1), 0);
co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer);
return true;
}
vec4 workbench_sample_texture(sampler2D image, vec2 coord, bool nearest_sampling)
{
vec2 tex_size = vec2(textureSize(image, 0).xy);
/* TODO(fclem) We could do the same with sampler objects.
* But this is a quick workaround instead of messing with the GPUTexture itself. */
vec2 uv = nearest_sampling ? (floor(coord * tex_size) + 0.5) / tex_size : coord;
return texture(image, uv);
}
vec4 workbench_sample_texture_array(sampler2DArray tile_array,
sampler1DArray tile_data,
vec2 coord,
bool nearest_sampling)
{
vec2 tex_size = vec2(textureSize(tile_array, 0).xy);
vec3 uv = vec3(coord, 0);
if (!node_tex_tile_lookup(uv, tile_array, tile_data))
return vec4(1.0, 0.0, 1.0, 1.0);
/* TODO(fclem) We could do the same with sampler objects.
* But this is a quick workaround instead of messing with the GPUTexture itself. */
uv.xy = nearest_sampling ? (floor(uv.xy * tex_size) + 0.5) / tex_size : uv.xy;
return texture(tile_array, uv);
}
uniform sampler2DArray imageTileArray;
uniform sampler1DArray imageTileData;
uniform sampler2D imageTexture;
uniform float imageTransparencyCutoff = 0.1;
uniform bool imageNearest;
uniform bool imagePremult;
vec3 workbench_image_color(vec2 uvs)
{
#ifdef V3D_SHADING_TEXTURE_COLOR
# ifdef TEXTURE_IMAGE_ARRAY
vec4 color = workbench_sample_texture_array(imageTileArray, imageTileData, uvs, imageNearest);
# else
vec4 color = workbench_sample_texture(imageTexture, uvs, imageNearest);
# endif
/* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */
if (imagePremult && !(color.a == 0.0 || color.a == 1.0)) {
color.rgb /= color.a;
}
# ifdef GPU_FRAGMENT_SHADER
if (color.a < imageTransparencyCutoff) {
discard;
}
# endif
return color.rgb;
#else
return vec3(1.0);
#endif
}

View File

@@ -0,0 +1,30 @@
#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped)
{
/* Quick creation of an orthonormal basis */
float a = 1.0 / (1.0 + I.z);
float b = -I.x * I.y * a;
vec3 b1 = vec3(1.0 - I.x * I.x * a, b, -I.x);
vec3 b2 = vec3(b, 1.0 - I.y * I.y * a, -I.y);
vec2 matcap_uv = vec2(dot(b1, N), dot(b2, N));
if (flipped) {
matcap_uv.x = -matcap_uv.x;
}
return matcap_uv * 0.496 + 0.5;
}
uniform sampler2D matcapDiffuseImage;
uniform sampler2D matcapSpecularImage;
vec3 get_matcap_lighting(vec3 base_color, vec3 N, vec3 I)
{
bool flipped = world_data.matcap_orientation != 0;
vec2 uv = matcap_uv_compute(I, N, flipped);
vec3 diffuse = textureLod(matcapDiffuseImage, uv, 0.0).rgb;
vec3 specular = textureLod(matcapSpecularImage, uv, 0.0).rgb;
return diffuse * base_color + specular * float(world_data.use_specular);
}

View File

@@ -0,0 +1,21 @@
layout(std140) uniform material_block
{
vec4 mat_data[4096];
};
/* If set to -1, the resource handle is used instead. */
uniform int materialIndex;
void workbench_material_data_get(
int handle, out vec3 color, out float alpha, out float roughness, out float metallic)
{
handle = (materialIndex != -1) ? materialIndex : handle;
vec4 data = mat_data[uint(handle) & 0xFFFu];
color = data.rgb;
uint encoded_data = floatBitsToUint(data.w);
alpha = float((encoded_data >> 16u) & 0xFFu) * (1.0 / 255.0);
roughness = float((encoded_data >> 8u) & 0xFFu) * (1.0 / 255.0);
metallic = float(encoded_data & 0xFFu) * (1.0 / 255.0);
}

View File

@@ -0,0 +1,18 @@
uniform sampler2D depthBuffer;
in vec4 uvcoordsvar;
out vec4 fragColor;
void main()
{
float depth = texture(depthBuffer, uvcoordsvar.st).r;
/* Discard background pixels. */
if (depth == 1.0) {
discard;
}
/* Make this fragment occlude any fragment that will try to
* render over it in the normal passes. */
gl_FragDepth = 0.0;
}

View File

@@ -1,12 +0,0 @@
#define OBJECT_OUTLINE_OFFSET 1
float calculate_object_outline(usampler2D objectId, ivec2 texel, uint object_id)
{
uvec4 oid_offset = uvec4(
texelFetchOffset(objectId, texel, 0, ivec2(0, OBJECT_OUTLINE_OFFSET)).r,
texelFetchOffset(objectId, texel, 0, ivec2(0, -OBJECT_OUTLINE_OFFSET)).r,
texelFetchOffset(objectId, texel, 0, ivec2(-OBJECT_OUTLINE_OFFSET, 0)).r,
texelFetchOffset(objectId, texel, 0, ivec2(OBJECT_OUTLINE_OFFSET, 0)).r);
return dot(vec4(equal(uvec4(object_id), oid_offset)), vec4(0.25));
}

View File

@@ -1,92 +1,29 @@
uniform vec4 materialColorAndMetal;
uniform float materialRoughness;
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_image_lib.glsl)
#ifdef TEXTURE_IMAGE_ARRAY
uniform sampler2DArray image_tile_array;
uniform sampler1DArray image_tile_data;
#else
uniform sampler2D image;
#endif
uniform float ImageTransparencyCutoff = 0.1;
uniform bool imageNearest;
uniform bool imagePremultiplied;
#ifdef NORMAL_VIEWPORT_PASS_ENABLED
in vec3 normal_viewport;
#endif
#ifdef V3D_SHADING_TEXTURE_COLOR
in vec2 uv_interp;
#endif
#ifdef V3D_SHADING_VERTEX_COLOR
in vec3 vertexColor;
#endif
#ifdef HAIR_SHADER
flat in float hair_rand;
#endif
#ifdef MATDATA_PASS_ENABLED
layout(location = 0) out vec4 materialData;
#endif
#ifdef OBJECT_ID_PASS_ENABLED
layout(location = 1) out uint objectId;
#endif
#ifdef NORMAL_VIEWPORT_PASS_ENABLED
layout(location = 2) out WB_Normal normalViewport;
#endif
layout(location = 1) out WB_Normal normalData;
layout(location = 2) out uint objectId;
uniform bool useMatcap = false;
void main()
{
#ifdef MATDATA_PASS_ENABLED
float metallic, roughness;
vec4 color;
normalData = workbench_normal_encode(gl_FrontFacing, normal_interp);
# if defined(V3D_SHADING_TEXTURE_COLOR)
# ifdef TEXTURE_IMAGE_ARRAY
color = workbench_sample_texture_array(
image_tile_array, image_tile_data, uv_interp, imageNearest, imagePremultiplied);
# else
color = workbench_sample_texture(image, uv_interp, imageNearest, imagePremultiplied);
# endif
if (color.a < ImageTransparencyCutoff) {
discard;
materialData = vec4(color_interp, packed_rough_metal);
objectId = uint(object_id);
if (useMatcap) {
/* For matcaps, save front facing in alpha channel. */
materialData.a = float(gl_FrontFacing);
}
# elif defined(V3D_SHADING_VERTEX_COLOR)
color.rgb = vertexColor;
# else
color.rgb = materialColorAndMetal.rgb;
# endif
# ifdef V3D_LIGHTING_MATCAP
/* Encode front facing in metallic channel. */
metallic = float(gl_FrontFacing);
roughness = 0.0;
# else
metallic = materialColorAndMetal.a;
roughness = materialRoughness;
# endif
# ifdef HAIR_SHADER
/* Add some variation to the hairs to avoid uniform look. */
float hair_variation = hair_rand * 0.1;
color = clamp(color - hair_variation, 0.0, 1.0);
metallic = clamp(materialColorAndMetal.a - hair_variation, 0.0, 1.0);
roughness = clamp(materialRoughness - hair_variation, 0.0, 1.0);
# endif
materialData.rgb = color.rgb;
materialData.a = workbench_float_pair_encode(roughness, metallic);
#endif /* MATDATA_PASS_ENABLED */
#ifdef OBJECT_ID_PASS_ENABLED
objectId = uint(resource_id + 1) & 0xFFu;
#endif
#ifdef NORMAL_VIEWPORT_PASS_ENABLED
vec3 n = (gl_FrontFacing) ? normal_viewport : -normal_viewport;
n = normalize(n);
normalViewport = workbench_normal_encode(n);
#ifdef V3D_SHADING_TEXTURE_COLOR
materialData.rgb = workbench_image_color(uv_interp);
#endif
}

View File

@@ -0,0 +1,94 @@
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_material_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_image_lib.glsl)
uniform samplerBuffer ac; /* active color layer */
uniform samplerBuffer au; /* active texture layer */
/* From http://libnoise.sourceforge.net/noisegen/index.html */
float integer_noise(int n)
{
n = (n >> 13) ^ n;
int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
return (float(nn) / 1073741824.0);
}
vec3 workbench_hair_random_normal(vec3 tan, vec3 binor, float rand)
{
/* To "simulate" anisotropic shading, randomize hair normal per strand. */
vec3 nor = cross(tan, binor);
nor = normalize(mix(nor, -tan, rand * 0.1));
float cos_theta = (rand * 2.0 - 1.0) * 0.2;
float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
nor = nor * sin_theta + binor * cos_theta;
return nor;
}
void workbench_hair_random_material(float rand,
inout vec3 color,
inout float roughness,
inout float metallic)
{
/* Center noise around 0. */
rand -= 0.5;
rand *= 0.1;
/* Add some variation to the hairs to avoid uniform look. */
metallic = clamp(metallic + rand, 0.0, 1.0);
roughness = clamp(roughness + rand, 0.0, 1.0);
/* Modulate by color intensity to reduce very high contrast when color is dark. */
color = clamp(color + rand * (color + 0.05), 0.0, 1.0);
}
void main()
{
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
float time, thick_time, thickness;
vec3 world_pos, tan, binor;
hair_get_pos_tan_binor_time(is_persp,
ModelMatrixInverse,
ViewMatrixInverse[3].xyz,
ViewMatrixInverse[2].xyz,
world_pos,
tan,
binor,
time,
thickness,
thick_time);
gl_Position = point_world_to_ndc(world_pos);
float hair_rand = integer_noise(hair_get_strand_id());
vec3 nor = workbench_hair_random_normal(tan, binor, hair_rand);
#ifdef USE_WORLD_CLIP_PLANES
world_clip_planes_calc_clip_distance(world_pos);
#endif
uv_interp = hair_get_customdata_vec2(au);
normal_interp = normalize(normal_world_to_view(nor));
#ifdef OPAQUE_MATERIAL
float metallic, roughness;
#endif
workbench_material_data_get(resource_handle, color_interp, alpha_interp, roughness, metallic);
if (materialIndex == 0) {
color_interp = hair_get_customdata_vec3(ac);
}
/* Hairs have lots of layer and can rapidly become the most prominent surface.
* So we lower their alpha artificially. */
alpha_interp *= 0.3;
workbench_hair_random_material(hair_rand, color_interp, roughness, metallic);
#ifdef OPAQUE_MATERIAL
packed_rough_metal = workbench_float_pair_encode(roughness, metallic);
#endif
object_id = int((uint(resource_id) + 1u) & 0xFFu);
}

View File

@@ -1,110 +1,40 @@
#ifndef HAIR_SHADER
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_material_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_image_lib.glsl)
in vec3 pos;
in vec3 nor;
in vec2 au; /* active texture layer */
# ifdef V3D_SHADING_VERTEX_COLOR
in vec4 ac; /* active color */
# endif
# define uv au
#else /* HAIR_SHADER */
# ifdef V3D_SHADING_TEXTURE_COLOR
uniform samplerBuffer au; /* active texture layer */
# endif
# ifdef V3D_SHADING_VERTEX_COLOR
uniform samplerBuffer ac; /* active color layer */
# endif
flat out float hair_rand;
#endif /* HAIR_SHADER */
#ifdef NORMAL_VIEWPORT_PASS_ENABLED
out vec3 normal_viewport;
#endif
#ifdef V3D_SHADING_TEXTURE_COLOR
out vec2 uv_interp;
#endif
#ifdef V3D_SHADING_VERTEX_COLOR
out vec3 vertexColor;
#endif
#ifdef OBJECT_ID_PASS_ENABLED
RESOURCE_ID_VARYING
#endif
/* From http://libnoise.sourceforge.net/noisegen/index.html */
float integer_noise(int n)
{
n = (n >> 13) ^ n;
int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
return (float(nn) / 1073741824.0);
}
vec3 workbench_hair_hair_normal(vec3 tan, vec3 binor, float rand)
{
/* To "simulate" anisotropic shading, randomize hair normal per strand. */
vec3 nor = cross(tan, binor);
nor = normalize(mix(nor, -tan, rand * 0.1));
float cos_theta = (rand * 2.0 - 1.0) * 0.2;
float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
nor = nor * sin_theta + binor * cos_theta;
return nor;
}
in vec2 au; /* active texture layer */
void main()
{
#ifdef HAIR_SHADER
# ifdef V3D_SHADING_TEXTURE_COLOR
vec2 uv = hair_get_customdata_vec2(au);
# endif
float time, thick_time, thickness;
vec3 world_pos, tan, binor;
hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0),
ModelMatrixInverse,
ViewMatrixInverse[3].xyz,
ViewMatrixInverse[2].xyz,
world_pos,
tan,
binor,
time,
thickness,
thick_time);
hair_rand = integer_noise(hair_get_strand_id());
vec3 nor = workbench_hair_hair_normal(tan, binor, hair_rand);
#else
vec3 world_pos = point_object_to_world(pos);
#endif
gl_Position = point_world_to_ndc(world_pos);
#ifdef V3D_SHADING_TEXTURE_COLOR
uv_interp = uv;
#endif
#ifdef V3D_SHADING_VERTEX_COLOR
# ifndef HAIR_SHADER
vertexColor = ac.rgb;
# else
vertexColor = hair_get_customdata_vec4(ac).rgb;
# endif
#endif
#ifdef NORMAL_VIEWPORT_PASS_ENABLED
# ifndef HAIR_SHADER
normal_viewport = normal_object_to_view(nor);
normal_viewport = normalize(normal_viewport);
# else
normal_viewport = normal_world_to_view(nor);
# endif
#endif
#ifdef OBJECT_ID_PASS_ENABLED
PASS_RESOURCE_ID
#endif
#ifdef USE_WORLD_CLIP_PLANES
world_clip_planes_calc_clip_distance(world_pos);
#endif
uv_interp = au;
normal_interp = normalize(normal_object_to_view(nor));
#ifdef OPAQUE_MATERIAL
float metallic, roughness;
#endif
workbench_material_data_get(resource_handle, color_interp, alpha_interp, roughness, metallic);
if (materialIndex == 0) {
color_interp = ac.rgb;
}
#ifdef OPAQUE_MATERIAL
packed_rough_metal = workbench_float_pair_encode(roughness, metallic);
#endif
object_id = int((uint(resource_id) + 1u) & 0xFFu);
}

View File

@@ -0,0 +1,21 @@
#ifdef GPU_VERTEX_SHADER
# define IN_OUT out
#else
# define IN_OUT in
#endif
IN_OUT ShaderStageInterface
{
vec3 normal_interp;
vec3 color_interp;
float alpha_interp;
vec2 uv_interp;
#ifdef TRANSPARENT_MATERIAL
flat float roughness;
flat float metallic;
#else
flat float packed_rough_metal;
#endif
flat int object_id;
};

View File

@@ -1,15 +1,19 @@
out vec4 fragColor;
layout(location = 0) out vec4 materialData;
layout(location = 1) out vec4 normalData;
layout(location = 2) out uint objectId;
void main()
{
const float intensity = 0.25;
const float a = 0.25;
#ifdef SHADOW_PASS
fragColor = vec4(
(gl_FrontFacing) ? vec3(intensity, -intensity, 0.0) : vec3(-intensity, intensity, 0.0), 1.0);
materialData.rgb = gl_FrontFacing ? vec3(a, -a, 0.0) : vec3(-a, a, 0.0);
#else
fragColor = vec4((gl_FrontFacing) ? vec3(intensity, intensity, -intensity) :
vec3(-intensity, -intensity, intensity),
1.0);
materialData.rgb = gl_FrontFacing ? vec3(a, a, -a) : vec3(-a, -a, a);
#endif
materialData.a = 0.0;
normalData = vec4(0.0);
objectId = 0u;
}

View File

@@ -0,0 +1,89 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_image_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_matcap_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_world_light_lib.glsl)
/* Revealage is actually stored in transparentAccum alpha channel.
* This is a workaround to older hardware not having separate blend equation per render target. */
layout(location = 0) out vec4 transparentAccum;
layout(location = 1) out vec4 revealageAccum;
/* Note: Blending will be skipped on objectId because output is a non-normalized integer buffer. */
layout(location = 2) out uint objectId;
/* Special function only to be used with calculate_transparent_weight(). */
float linear_zdepth(float depth, vec4 viewvecs[3], mat4 proj_mat)
{
if (proj_mat[3][3] == 0.0) {
float d = 2.0 * depth - 1.0;
return -proj_mat[3][2] / (d + proj_mat[2][2]);
}
else {
/* Return depth from near plane. */
return depth * viewvecs[1].z;
}
}
/* Based on :
* McGuire and Bavoil, Weighted Blended Order-Independent Transparency, Journal of
* Computer Graphics Techniques (JCGT), vol. 2, no. 2, 122141, 2013
*/
float calculate_transparent_weight(void)
{
float z = linear_zdepth(gl_FragCoord.z, world_data.viewvecs, ProjectionMatrix);
#if 0
/* Eq 10 : Good for surfaces with varying opacity (like particles) */
float a = min(1.0, alpha * 10.0) + 0.01;
float b = -gl_FragCoord.z * 0.95 + 1.0;
float w = a * a * a * 3e2 * b * b * b;
#else
/* Eq 7 put more emphasis on surfaces closer to the view. */
// float w = 10.0 / (1e-5 + pow(abs(z) / 5.0, 2.0) + pow(abs(z) / 200.0, 6.0)); /* Eq 7 */
// float w = 10.0 / (1e-5 + pow(abs(z) / 10.0, 3.0) + pow(abs(z) / 200.0, 6.0)); /* Eq 8 */
// float w = 10.0 / (1e-5 + pow(abs(z) / 200.0, 4.0)); /* Eq 9 */
/* Same as eq 7, but optimized. */
float a = abs(z) / 5.0;
float b = abs(z) / 200.0;
b *= b;
float w = 10.0 / ((1e-5 + a * a) + b * (b * b)); /* Eq 7 */
#endif
return clamp(w, 1e-2, 3e2);
}
void main()
{
/* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */
vec2 uv_viewport = gl_FragCoord.xy * world_data.viewport_size_inv;
vec3 I = view_vector_from_screen_uv(uv_viewport, world_data.viewvecs, ProjectionMatrix);
vec3 N = normalize(normal_interp);
vec3 color = color_interp;
#ifdef V3D_SHADING_TEXTURE_COLOR
color = workbench_image_color(uv_interp);
#endif
#ifdef V3D_LIGHTING_MATCAP
vec3 shaded_color = get_matcap_lighting(color, N, I);
#endif
#ifdef V3D_LIGHTING_STUDIO
vec3 shaded_color = get_world_lighting(color, roughness, metallic, N, I);
#endif
#ifdef V3D_LIGHTING_FLAT
vec3 shaded_color = color;
#endif
shaded_color *= get_shadow(N);
/* Listing 4 */
float weight = calculate_transparent_weight() * alpha_interp;
transparentAccum = vec4(shaded_color * weight, alpha_interp);
revealageAccum = vec4(weight);
objectId = uint(object_id);
}

View File

@@ -0,0 +1,26 @@
uniform sampler2D transparentAccum;
uniform sampler2D transparentRevealage;
in vec4 uvcoordsvar;
out vec4 fragColor;
/* Based on :
* McGuire and Bavoil, Weighted Blended Order-Independent Transparency, Journal of
* Computer Graphics Techniques (JCGT), vol. 2, no. 2, 122141, 2013
*/
void main()
{
/* Revealage is actually stored in transparentAccum alpha channel.
* This is a workaround to older hardware not having separate blend equation per render target.
*/
vec4 trans_accum = texture(transparentAccum, uvcoordsvar.st);
float trans_weight = texture(transparentRevealage, uvcoordsvar.st).r;
float trans_reveal = trans_accum.a;
/* Listing 4 */
fragColor.rgb = trans_accum.rgb / clamp(trans_weight, 1e-4, 5e4);
fragColor.a = 1.0 - trans_reveal;
}

View File

@@ -1,4 +1,9 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_common_obinfos_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
uniform sampler2D depthBuffer;
uniform sampler3D densityTexture;
@@ -8,10 +13,9 @@ uniform sampler1D flameColorTexture;
uniform sampler1D transferTexture;
uniform int samplesLen = 256;
uniform float noiseOfs = 0.0f;
uniform float noiseOfs = 0.0;
uniform float stepLength; /* Step length in local space. */
uniform float densityScale; /* Simple Opacity multiplicator. */
uniform vec4 viewvecs[3];
uniform vec3 activeColor;
uniform float slicePosition;
@@ -23,34 +27,11 @@ in vec3 localPos;
out vec4 fragColor;
#define M_PI 3.1415926535897932 /* pi */
float phase_function_isotropic()
{
return 1.0 / (4.0 * M_PI);
}
float get_view_z_from_depth(float depth)
{
if (ProjectionMatrix[3][3] == 0.0) {
float d = 2.0 * depth - 1.0;
return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]);
}
else {
return viewvecs[0].z + depth * viewvecs[1].z;
}
}
vec3 get_view_space_from_depth(vec2 uvcoords, float depth)
{
if (ProjectionMatrix[3][3] == 0.0) {
return vec3(viewvecs[0].xy + uvcoords * viewvecs[1].xy, 1.0) * get_view_z_from_depth(depth);
}
else {
return viewvecs[0].xyz + vec3(uvcoords, depth) * viewvecs[1].xyz;
}
}
float max_v3(vec3 v)
{
return max(v.x, max(v.y, v.z));
@@ -209,8 +190,10 @@ void main()
float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r;
float depth_end = min(depth, gl_FragCoord.z);
vec3 vs_ray_end = get_view_space_from_depth(screen_uv, depth_end);
vec3 vs_ray_ori = get_view_space_from_depth(screen_uv, 0.0);
vec3 vs_ray_end = view_position_from_depth(
screen_uv, depth_end, world_data.viewvecs, ProjectionMatrix);
vec3 vs_ray_ori = view_position_from_depth(
screen_uv, 0.0, world_data.viewvecs, ProjectionMatrix);
vec3 vs_ray_dir = (is_persp) ? (vs_ray_end - vs_ray_ori) : vec3(0.0, 0.0, -1.0);
vs_ray_dir /= abs(vs_ray_dir.z);

View File

@@ -1,4 +1,7 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_common_obinfos_lib.glsl)
uniform float slicePosition;
uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */

View File

@@ -1,4 +1,6 @@
#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
/* [Drobot2014a] Low Level Optimizations for GCN */
vec4 fast_rcp(vec4 v)
{
@@ -41,9 +43,19 @@ vec4 wrapped_lighting(vec4 NL, vec4 w)
return clamp((NL + w) * denom, 0.0, 1.0);
}
vec3 get_world_lighting(
WorldData world_data, vec3 diffuse_color, vec3 specular_color, float roughness, vec3 N, vec3 I)
vec3 get_world_lighting(vec3 base_color, float roughness, float metallic, vec3 N, vec3 I)
{
vec3 specular_color, diffuse_color;
if (world_data.use_specular) {
diffuse_color = mix(base_color, vec3(0.0), metallic);
specular_color = mix(vec3(0.05), base_color, metallic);
}
else {
diffuse_color = base_color;
specular_color = vec3(0.0);
}
vec3 specular_light = world_data.ambient_color.rgb;
vec3 diffuse_light = world_data.ambient_color.rgb;
vec4 wrap = vec4(world_data.lights[0].diffuse_color_wrap.a,
@@ -51,37 +63,37 @@ vec3 get_world_lighting(
world_data.lights[2].diffuse_color_wrap.a,
world_data.lights[3].diffuse_color_wrap.a);
#ifdef V3D_SHADING_SPECULAR_HIGHLIGHT
/* Prepare Specular computation. Eval 4 lights at once. */
vec3 R = -reflect(I, N);
vec4 spec_angle, spec_NL, wrap_NL;
prep_specular(world_data.lights[0].direction.xyz, I, N, R, spec_NL.x, wrap_NL.x, spec_angle.x);
prep_specular(world_data.lights[1].direction.xyz, I, N, R, spec_NL.y, wrap_NL.y, spec_angle.y);
prep_specular(world_data.lights[2].direction.xyz, I, N, R, spec_NL.z, wrap_NL.z, spec_angle.z);
prep_specular(world_data.lights[3].direction.xyz, I, N, R, spec_NL.w, wrap_NL.w, spec_angle.w);
if (world_data.use_specular) {
/* Prepare Specular computation. Eval 4 lights at once. */
vec3 R = -reflect(I, N);
vec4 spec_angle, spec_NL, wrap_NL;
prep_specular(world_data.lights[0].direction.xyz, I, N, R, spec_NL.x, wrap_NL.x, spec_angle.x);
prep_specular(world_data.lights[1].direction.xyz, I, N, R, spec_NL.y, wrap_NL.y, spec_angle.y);
prep_specular(world_data.lights[2].direction.xyz, I, N, R, spec_NL.z, wrap_NL.z, spec_angle.z);
prep_specular(world_data.lights[3].direction.xyz, I, N, R, spec_NL.w, wrap_NL.w, spec_angle.w);
vec4 gloss = vec4(1.0 - roughness);
/* Reduce gloss for smooth light. (simulate bigger light) */
gloss *= 1.0 - wrap;
vec4 shininess = exp2(10.0 * gloss + 1.0);
vec4 gloss = vec4(1.0 - roughness);
/* Reduce gloss for smooth light. (simulate bigger light) */
gloss *= 1.0 - wrap;
vec4 shininess = exp2(10.0 * gloss + 1.0);
vec4 spec_light = blinn_specular(shininess, spec_angle, spec_NL);
vec4 spec_light = blinn_specular(shininess, spec_angle, spec_NL);
/* Simulate Env. light. */
vec4 w = mix(wrap, vec4(1.0), roughness);
vec4 spec_env = wrapped_lighting(wrap_NL, w);
/* Simulate Env. light. */
vec4 w = mix(wrap, vec4(1.0), roughness);
vec4 spec_env = wrapped_lighting(wrap_NL, w);
spec_light = mix(spec_light, spec_env, wrap * wrap);
spec_light = mix(spec_light, spec_env, wrap * wrap);
/* Multiply result by lights specular colors. */
specular_light += spec_light.x * world_data.lights[0].specular_color.rgb;
specular_light += spec_light.y * world_data.lights[1].specular_color.rgb;
specular_light += spec_light.z * world_data.lights[2].specular_color.rgb;
specular_light += spec_light.w * world_data.lights[3].specular_color.rgb;
/* Multiply result by lights specular colors. */
specular_light += spec_light.x * world_data.lights[0].specular_color.rgb;
specular_light += spec_light.y * world_data.lights[1].specular_color.rgb;
specular_light += spec_light.z * world_data.lights[2].specular_color.rgb;
specular_light += spec_light.w * world_data.lights[3].specular_color.rgb;
float NV = clamp(dot(N, I), 0.0, 1.0);
specular_color = brdf_approx(specular_color, roughness, NV);
#endif
float NV = clamp(dot(N, I), 0.0, 1.0);
specular_color = brdf_approx(specular_color, roughness, NV);
}
specular_light *= specular_color;
/* Prepare diffuse computation. Eval 4 lights at once. */
@@ -107,3 +119,13 @@ vec3 get_world_lighting(
return diffuse_light + specular_light;
}
uniform bool forceShadowing = false;
float get_shadow(vec3 N)
{
float light_factor = -dot(N, world_data.shadow_direction_vs.xyz);
float shadow_mix = smoothstep(world_data.shadow_shift, world_data.shadow_focus, light_factor);
shadow_mix *= forceShadowing ? 0.0 : world_data.shadow_mul;
return shadow_mix + world_data.shadow_add;
}

View File

@@ -1,119 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Simple engine for drawing color and/or depth.
* When we only need simple studio shaders.
*/
#include "DRW_render.h"
#include "GPU_shader.h"
#include "RE_pipeline.h"
#include "workbench_private.h"
/* Functions */
static void workbench_solid_engine_init(void *vedata)
{
WORKBENCH_Data *data = vedata;
workbench_deferred_engine_init(data);
}
static void workbench_solid_cache_init(void *vedata)
{
WORKBENCH_Data *data = vedata;
workbench_deferred_cache_init(data);
}
static void workbench_solid_cache_populate(void *vedata, Object *ob)
{
WORKBENCH_Data *data = vedata;
workbench_deferred_solid_cache_populate(data, ob);
}
static void workbench_solid_cache_finish(void *vedata)
{
WORKBENCH_Data *data = vedata;
workbench_deferred_cache_finish(data);
}
static void workbench_solid_draw_scene(void *vedata)
{
WORKBENCH_Data *data = vedata;
const int num_samples = workbench_num_viewport_rendering_iterations(data);
for (int sample = 0; sample < num_samples; sample++) {
workbench_deferred_draw_scene(data);
}
workbench_deferred_draw_finish(data);
}
static void workbench_solid_engine_free(void)
{
workbench_deferred_engine_free();
}
static void workbench_solid_view_update(void *vedata)
{
WORKBENCH_Data *data = vedata;
workbench_taa_view_updated(data);
}
static void workbench_solid_id_update(void *UNUSED(vedata), struct ID *id)
{
if (GS(id->name) == ID_OB) {
WORKBENCH_ObjectData *oed = (WORKBENCH_ObjectData *)DRW_drawdata_get(
id, &draw_engine_workbench_solid);
if (oed != NULL && oed->dd.recalc != 0) {
oed->shadow_bbox_dirty = (oed->dd.recalc & ID_RECALC_ALL) != 0;
oed->dd.recalc = 0;
}
}
}
static void workbench_render_to_image(void *vedata,
RenderEngine *engine,
RenderLayer *render_layer,
const rcti *rect)
{
workbench_render(vedata, engine, render_layer, rect);
}
static const DrawEngineDataSize workbench_data_size = DRW_VIEWPORT_DATA_SIZE(WORKBENCH_Data);
DrawEngineType draw_engine_workbench_solid = {
NULL,
NULL,
N_("Workbench"),
&workbench_data_size,
&workbench_solid_engine_init,
&workbench_solid_engine_free,
&workbench_solid_cache_init,
&workbench_solid_cache_populate,
&workbench_solid_cache_finish,
&workbench_solid_draw_scene,
&workbench_solid_view_update,
&workbench_solid_id_update,
&workbench_render_to_image,
};

View File

@@ -1,97 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Simple engine for drawing color and/or depth.
* When we only need simple studio shaders.
*/
#include "DRW_render.h"
#include "GPU_shader.h"
#include "workbench_private.h"
/* Functions */
static void workbench_transparent_engine_init(void *vedata)
{
WORKBENCH_Data *data = vedata;
workbench_forward_engine_init(data);
}
static void workbench_transparent_cache_init(void *vedata)
{
WORKBENCH_Data *data = vedata;
workbench_forward_cache_init(data);
}
static void workbench_transparent_cache_populate(void *vedata, Object *ob)
{
WORKBENCH_Data *data = vedata;
workbench_forward_cache_populate(data, ob);
}
static void workbench_transparent_cache_finish(void *vedata)
{
WORKBENCH_Data *data = vedata;
workbench_forward_cache_finish(data);
}
static void workbench_transparent_draw_scene(void *vedata)
{
WORKBENCH_Data *data = vedata;
const int num_samples = workbench_num_viewport_rendering_iterations(data);
for (int sample = 0; sample < num_samples; sample++) {
workbench_forward_draw_scene(data);
}
workbench_forward_draw_finish(data);
}
static void workbench_transparent_engine_free(void)
{
workbench_forward_engine_free();
}
static void workbench_transparent_view_update(void *vedata)
{
WORKBENCH_Data *data = vedata;
workbench_taa_view_updated(data);
}
static const DrawEngineDataSize workbench_data_size = DRW_VIEWPORT_DATA_SIZE(WORKBENCH_Data);
DrawEngineType draw_engine_workbench_transparent = {
NULL,
NULL,
N_("Workbench"),
&workbench_data_size,
&workbench_transparent_engine_init,
&workbench_transparent_engine_free,
&workbench_transparent_cache_init,
&workbench_transparent_cache_populate,
&workbench_transparent_cache_finish,
&workbench_transparent_draw_scene,
&workbench_transparent_view_update,
NULL,
NULL,
};

View File

@@ -22,133 +22,231 @@
#include "workbench_private.h"
#include "BLI_memblock.h"
#include "DNA_userdef_types.h"
#include "ED_view3d.h"
#include "ED_screen.h"
#include "UI_resources.h"
#include "GPU_batch.h"
#include "GPU_uniformbuffer.h"
/* -------------------------------------------------------------------- */
/** \name World Data
* \{ */
static void workbench_world_data_free(DrawData *dd)
GPUUniformBuffer *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd)
{
WORKBENCH_WorldData *data = (WORKBENCH_WorldData *)dd;
DRW_UBO_FREE_SAFE(data->world_ubo);
struct GPUUniformBuffer **ubo = BLI_memblock_alloc(wpd->material_ubo);
if (*ubo == NULL) {
*ubo = GPU_uniformbuffer_create(sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL, NULL, NULL);
}
return *ubo;
}
/* Ensure the availability of the world_ubo in the given WORKBENCH_PrivateData
*
* See T70167: Some platforms create threads to upload ubo's.
*
* Reuses the last previous created `world_ubo`. Due to limitations of
* DrawData it will only be reused when there is a world attached to the Scene.
* Future development: The best location would be to store it in the View3D.
*
* We don't cache the data itself as there was no indication that that lead to
* an improvement.
*
* This functions also sets the `WORKBENCH_PrivateData.is_world_ubo_owner` that must
* be respected.
*/
static void workbench_world_data_ubo_ensure(const Scene *scene, WORKBENCH_PrivateData *wpd)
static void workbench_ubo_free(void *elem)
{
World *world = scene->world;
if (world) {
WORKBENCH_WorldData *engine_world_data = (WORKBENCH_WorldData *)DRW_drawdata_ensure(
&world->id,
&draw_engine_workbench_solid,
sizeof(WORKBENCH_WorldData),
NULL,
&workbench_world_data_free);
if (engine_world_data->world_ubo == NULL) {
engine_world_data->world_ubo = DRW_uniformbuffer_create(sizeof(WORKBENCH_UBO_World),
&wpd->world_data);
}
else {
DRW_uniformbuffer_update(engine_world_data->world_ubo, &wpd->world_data);
}
/* Borrow world data ubo */
wpd->is_world_ubo_owner = false;
wpd->world_ubo = engine_world_data->world_ubo;
}
else {
/* there is no world so we cannot cache the UBO. */
BLI_assert(!wpd->world_ubo || wpd->is_world_ubo_owner);
if (!wpd->world_ubo) {
wpd->is_world_ubo_owner = true;
wpd->world_ubo = DRW_uniformbuffer_create(sizeof(WORKBENCH_UBO_World), &wpd->world_data);
}
}
GPUUniformBuffer **ubo = elem;
DRW_UBO_FREE_SAFE(*ubo);
}
static void workbench_world_data_update_shadow_direction_vs(WORKBENCH_PrivateData *wpd)
static void workbench_view_layer_data_free(void *storage)
{
WORKBENCH_UBO_World *wd = &wpd->world_data;
float light_direction[3];
float view_matrix[4][4];
DRW_view_viewmat_get(NULL, view_matrix, false);
WORKBENCH_ViewLayerData *vldata = (WORKBENCH_ViewLayerData *)storage;
workbench_private_data_get_light_direction(light_direction);
DRW_UBO_FREE_SAFE(vldata->dof_sample_ubo);
DRW_UBO_FREE_SAFE(vldata->world_ubo);
DRW_UBO_FREE_SAFE(vldata->cavity_sample_ubo);
DRW_TEXTURE_FREE_SAFE(vldata->cavity_jitter_tx);
/* Shadow direction. */
mul_v3_mat3_m4v3(wd->shadow_direction_vs, view_matrix, light_direction);
BLI_memblock_destroy(vldata->material_ubo_data, NULL);
BLI_memblock_destroy(vldata->material_ubo, workbench_ubo_free);
}
static WORKBENCH_ViewLayerData *workbench_view_layer_data_ensure_ex(struct ViewLayer *view_layer)
{
WORKBENCH_ViewLayerData **vldata = (WORKBENCH_ViewLayerData **)
DRW_view_layer_engine_data_ensure_ex(view_layer,
(DrawEngineType *)&workbench_view_layer_data_ensure_ex,
&workbench_view_layer_data_free);
if (*vldata == NULL) {
*vldata = MEM_callocN(sizeof(**vldata), "WORKBENCH_ViewLayerData");
size_t matbuf_size = sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL;
(*vldata)->material_ubo_data = BLI_memblock_create_ex(matbuf_size, matbuf_size * 2);
(*vldata)->material_ubo = BLI_memblock_create_ex(sizeof(void *), sizeof(void *) * 8);
(*vldata)->world_ubo = DRW_uniformbuffer_create(sizeof(WORKBENCH_UBO_World), NULL);
}
return *vldata;
}
/* \} */
void workbench_clear_color_get(float color[4])
static void workbench_viewvecs_update(float r_viewvecs[3][4])
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
float invproj[4][4];
const bool is_persp = DRW_view_is_persp_get(NULL);
DRW_view_winmat_get(NULL, invproj, true);
if (!DRW_state_is_scene_render() || !DRW_state_draw_background()) {
zero_v4(color);
/* view vectors for the corners of the view frustum.
* Can be used to recreate the world space position easily */
copy_v4_fl4(r_viewvecs[0], -1.0f, -1.0f, -1.0f, 1.0f);
copy_v4_fl4(r_viewvecs[1], 1.0f, -1.0f, -1.0f, 1.0f);
copy_v4_fl4(r_viewvecs[2], -1.0f, 1.0f, -1.0f, 1.0f);
/* convert the view vectors to view space */
for (int i = 0; i < 3; i++) {
mul_m4_v4(invproj, r_viewvecs[i]);
/* normalized trick see:
* http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */
mul_v3_fl(r_viewvecs[i], 1.0f / r_viewvecs[i][3]);
if (is_persp) {
mul_v3_fl(r_viewvecs[i], 1.0f / r_viewvecs[i][2]);
}
r_viewvecs[i][3] = 1.0;
}
else if (scene->world) {
copy_v3_v3(color, &scene->world->horr);
color[3] = 1.0f;
}
else {
zero_v3(color);
color[3] = 1.0f;
/* we need to store the differences */
r_viewvecs[1][0] -= r_viewvecs[0][0];
r_viewvecs[1][1] = r_viewvecs[2][1] - r_viewvecs[0][1];
/* calculate a depth offset as well */
if (!is_persp) {
float vec_far[] = {-1.0f, -1.0f, 1.0f, 1.0f};
mul_m4_v4(invproj, vec_far);
mul_v3_fl(vec_far, 1.0f / vec_far[3]);
r_viewvecs[1][2] = vec_far[2] - r_viewvecs[0][2];
}
}
void workbench_effect_info_init(WORKBENCH_EffectInfo *effect_info)
static void workbench_studiolight_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd)
{
effect_info->jitter_index = 0;
effect_info->view_updated = true;
StudioLight *studiolight = wpd->studio_light;
float view_matrix[4][4], rot_matrix[4][4];
DRW_view_viewmat_get(NULL, view_matrix, false);
if (USE_WORLD_ORIENTATION(wpd)) {
axis_angle_to_mat4_single(rot_matrix, 'Z', -wpd->shading.studiolight_rot_z);
mul_m4_m4m4(rot_matrix, view_matrix, rot_matrix);
swap_v3_v3(rot_matrix[2], rot_matrix[1]);
negate_v3(rot_matrix[2]);
}
else {
unit_m4(rot_matrix);
}
if (U.edit_studio_light) {
studiolight = BKE_studiolight_studio_edit_get();
}
/* Studio Lights. */
for (int i = 0; i < 4; i++) {
WORKBENCH_UBO_Light *light = &wd->lights[i];
SolidLight *sl = (studiolight) ? &studiolight->light[i] : NULL;
if (sl && sl->flag) {
copy_v3_v3(light->light_direction, sl->vec);
mul_mat3_m4_v3(rot_matrix, light->light_direction);
/* We should predivide the power by PI but that makes the lights really dim. */
copy_v3_v3(light->specular_color, sl->spec);
copy_v3_v3(light->diffuse_color, sl->col);
light->wrapped = sl->smooth;
}
else {
copy_v3_fl3(light->light_direction, 1.0f, 0.0f, 0.0f);
copy_v3_fl(light->specular_color, 0.0f);
copy_v3_fl(light->diffuse_color, 0.0f);
}
}
if (studiolight) {
copy_v3_v3(wd->ambient_color, studiolight->light_ambient);
}
else {
copy_v3_fl(wd->ambient_color, 1.0f);
}
wd->use_specular = workbench_is_specular_highlight_enabled(wpd);
}
void workbench_private_data_init(WORKBENCH_PrivateData *wpd)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
wpd->material_hash = BLI_ghash_ptr_new(__func__);
wpd->material_transp_hash = BLI_ghash_ptr_new(__func__);
wpd->preferences = &U;
View3D *v3d = draw_ctx->v3d;
RegionView3D *rv3d = draw_ctx->rv3d;
View3D *v3d = draw_ctx->v3d;
Scene *scene = draw_ctx->scene;
WORKBENCH_ViewLayerData *vldata = workbench_view_layer_data_ensure_ex(draw_ctx->view_layer);
wpd->is_playback = DRW_state_is_playback();
wpd->is_navigating = rv3d && (rv3d->rflag & (RV3D_NAVIGATING | RV3D_PAINTING));
wpd->ctx_mode = CTX_data_mode_enum_ex(
draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode);
wpd->preferences = &U;
wpd->scene = scene;
wpd->sh_cfg = draw_ctx->sh_cfg;
wpd->clip_state = RV3D_CLIPPING_ENABLED(v3d, rv3d) ? DRW_STATE_CLIP_PLANES : 0;
wpd->cull_state = CULL_BACKFACE_ENABLED(wpd) ? DRW_STATE_CULL_BACK : 0;
wpd->vldata = vldata;
wpd->world_ubo = vldata->world_ubo;
wpd->taa_sample_len = workbench_antialiasing_sample_count_get(wpd);
wpd->volumes_do = false;
BLI_listbase_clear(&wpd->smoke_domains);
if (!v3d || (v3d->shading.type == OB_RENDER && BKE_scene_uses_blender_workbench(scene))) {
/* FIXME: This reproduce old behavior when workbench was separated in 2 engines.
* But this is a workaround for a missing update tagging from operators. */
if (scene->display.shading.type != wpd->shading.type ||
XRAY_ENABLED(v3d) != XRAY_ENABLED((&scene->display))) {
wpd->view_updated = true;
}
wpd->shading = scene->display.shading;
wpd->shading.xray_alpha = XRAY_ALPHA((&scene->display));
wpd->use_color_render_settings = true;
if (XRAY_FLAG_ENABLED((&scene->display))) {
wpd->shading.xray_alpha = XRAY_ALPHA((&scene->display));
}
else {
wpd->shading.xray_alpha = 1.0f;
}
if (scene->r.alphamode == R_ALPHAPREMUL) {
copy_v4_fl(wpd->background_color, 0.0f);
}
else if (scene->world) {
World *wo = scene->world;
copy_v4_fl4(wpd->background_color, wo->horr, wo->horg, wo->horb, 1.0f);
}
else {
copy_v4_fl4(wpd->background_color, 0.0f, 0.0f, 0.0f, 1.0f);
}
}
else {
wpd->shading = v3d->shading;
wpd->shading.xray_alpha = XRAY_ALPHA(v3d);
wpd->use_color_render_settings = false;
}
/* FIXME: This reproduce old behavior when workbench was separated in 2 engines.
* But this is a workaround for a missing update tagging from operators. */
if (v3d->shading.type != wpd->shading.type || XRAY_ENABLED(v3d) != XRAY_ENABLED(wpd)) {
wpd->view_updated = true;
}
wpd->use_color_management = BKE_scene_check_color_management_enabled(scene);
wpd->shading = v3d->shading;
if (wpd->shading.type < OB_SOLID) {
wpd->shading.xray_alpha = 0.0f;
}
else if (XRAY_ENABLED(v3d)) {
wpd->shading.xray_alpha = XRAY_ALPHA(v3d);
}
else {
wpd->shading.xray_alpha = 1.0f;
}
/* No background. The overlays will draw the correct one. */
copy_v4_fl(wpd->background_color, 0.0f);
}
if (wpd->shading.light == V3D_LIGHTING_MATCAP) {
wpd->studio_light = BKE_studiolight_find(wpd->shading.matcap, STUDIOLIGHT_TYPE_MATCAP);
@@ -162,119 +260,56 @@ void workbench_private_data_init(WORKBENCH_PrivateData *wpd)
wpd->studio_light = BKE_studiolight_find(wpd->shading.studio_light, STUDIOLIGHT_TYPE_STUDIO);
}
float shadow_focus = scene->display.shadow_focus;
/* Clamp to avoid overshadowing and shading errors. */
CLAMP(shadow_focus, 0.0001f, 0.99999f);
wpd->shadow_shift = scene->display.shadow_shift;
wpd->shadow_focus = 1.0f - shadow_focus * (1.0f - wpd->shadow_shift);
wpd->shadow_multiplier = 1.0 - wpd->shading.shadow_intensity;
WORKBENCH_UBO_World *wd = &wpd->world_data;
wd->matcap_orientation = (wpd->shading.flag & V3D_SHADING_MATCAP_FLIP_X) != 0;
studiolight_update_world(wpd, wpd->studio_light, wd);
copy_v3_v3(wd->object_outline_color, wpd->shading.object_outline_color);
wd->object_outline_color[3] = 1.0f;
wd->curvature_ridge = 0.5f / max_ff(square_f(wpd->shading.curvature_ridge_factor), 1e-4f);
wd->curvature_valley = 0.7f / max_ff(square_f(wpd->shading.curvature_valley_factor), 1e-4f);
/* Will be NULL when rendering. */
if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
wpd->world_clip_planes = rv3d->clip;
}
else {
wpd->world_clip_planes = NULL;
}
workbench_world_data_update_shadow_direction_vs(wpd);
workbench_world_data_ubo_ensure(scene, wpd);
/* Cavity settings */
{
const int ssao_samples = scene->display.matcap_ssao_samples;
float invproj[4][4];
const bool is_persp = DRW_view_is_persp_get(NULL);
/* view vectors for the corners of the view frustum.
* Can be used to recreate the world space position easily */
float viewvecs[3][4] = {
{-1.0f, -1.0f, -1.0f, 1.0f},
{1.0f, -1.0f, -1.0f, 1.0f},
{-1.0f, 1.0f, -1.0f, 1.0f},
};
int i;
const float *size = DRW_viewport_size_get();
wpd->ssao_params[0] = ssao_samples;
wpd->ssao_params[1] = size[0] / 64.0;
wpd->ssao_params[2] = size[1] / 64.0;
wpd->ssao_params[3] = 0;
/* distance, factor, factor, attenuation */
copy_v4_fl4(wpd->ssao_settings,
scene->display.matcap_ssao_distance,
wpd->shading.cavity_valley_factor,
wpd->shading.cavity_ridge_factor,
scene->display.matcap_ssao_attenuation);
DRW_view_winmat_get(NULL, wpd->winmat, false);
DRW_view_winmat_get(NULL, invproj, true);
/* convert the view vectors to view space */
for (i = 0; i < 3; i++) {
mul_m4_v4(invproj, viewvecs[i]);
/* normalized trick see:
* http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */
mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][3]);
if (is_persp) {
mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][2]);
}
viewvecs[i][3] = 1.0;
copy_v4_v4(wpd->viewvecs[i], viewvecs[i]);
}
/* we need to store the differences */
wpd->viewvecs[1][0] -= wpd->viewvecs[0][0];
wpd->viewvecs[1][1] = wpd->viewvecs[2][1] - wpd->viewvecs[0][1];
/* calculate a depth offset as well */
if (!is_persp) {
float vec_far[] = {-1.0f, -1.0f, 1.0f, 1.0f};
mul_m4_v4(invproj, vec_far);
mul_v3_fl(vec_far, 1.0f / vec_far[3]);
wpd->viewvecs[1][2] = vec_far[2] - wpd->viewvecs[0][2];
}
/* Material UBOs. */
wpd->material_ubo_data = vldata->material_ubo_data;
wpd->material_ubo = vldata->material_ubo;
wpd->material_chunk_count = 1;
wpd->material_chunk_curr = 0;
wpd->material_index = 1;
/* Create default material ubo. */
wpd->material_ubo_data_curr = BLI_memblock_alloc(wpd->material_ubo_data);
wpd->material_ubo_curr = workbench_material_ubo_alloc(wpd);
/* Init default material used by vertex color & texture. */
workbench_material_ubo_data(
wpd, NULL, NULL, &wpd->material_ubo_data_curr[0], V3D_SHADING_MATERIAL_COLOR);
}
wpd->volumes_do = false;
BLI_listbase_clear(&wpd->smoke_domains);
}
void workbench_private_data_get_light_direction(float r_light_direction[3])
void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd)
{
WORKBENCH_UBO_World wd;
copy_v2_v2(wd.viewport_size, DRW_viewport_size_get());
copy_v2_v2(wd.viewport_size_inv, DRW_viewport_invert_size_get());
copy_v3_v3(wd.object_outline_color, wpd->shading.object_outline_color);
wd.object_outline_color[3] = 1.0f;
wd.ui_scale = G_draw.block.sizePixel;
wd.matcap_orientation = (wpd->shading.flag & V3D_SHADING_MATCAP_FLIP_X) != 0;
workbench_studiolight_data_update(wpd, &wd);
workbench_shadow_data_update(wpd, &wd);
workbench_cavity_data_update(wpd, &wd);
workbench_viewvecs_update(wd.viewvecs);
DRW_uniformbuffer_update(wpd->world_ubo, &wd);
}
void workbench_update_material_ubos(WORKBENCH_PrivateData *UNUSED(wpd))
{
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
WORKBENCH_ViewLayerData *vldata = workbench_view_layer_data_ensure_ex(draw_ctx->view_layer);
copy_v3_v3(r_light_direction, scene->display.light_direction);
SWAP(float, r_light_direction[2], r_light_direction[1]);
r_light_direction[2] = -r_light_direction[2];
r_light_direction[0] = -r_light_direction[0];
}
void workbench_private_data_free(WORKBENCH_PrivateData *wpd)
{
BLI_ghash_free(wpd->material_hash, NULL, MEM_freeN);
BLI_ghash_free(wpd->material_transp_hash, NULL, MEM_freeN);
if (wpd->is_world_ubo_owner) {
DRW_UBO_FREE_SAFE(wpd->world_ubo);
}
else {
wpd->world_ubo = NULL;
BLI_memblock_iter iter, iter_data;
BLI_memblock_iternew(vldata->material_ubo, &iter);
BLI_memblock_iternew(vldata->material_ubo_data, &iter_data);
WORKBENCH_UBO_Material *matchunk;
while ((matchunk = BLI_memblock_iterstep(&iter_data))) {
GPUUniformBuffer **ubo = BLI_memblock_iterstep(&iter);
BLI_assert(*ubo != NULL);
GPU_uniformbuffer_update(*ubo, matchunk);
}
DRW_UBO_FREE_SAFE(wpd->dof_ubo);
BLI_memblock_clear(vldata->material_ubo, workbench_ubo_free);
BLI_memblock_clear(vldata->material_ubo_data, NULL);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,101 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
#include "ED_screen.h"
#include "draw_color_management.h"
#include "workbench_private.h"
void workbench_aa_create_pass(WORKBENCH_Data *vedata, GPUTexture **tx)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PrivateData *wpd = stl->g_data;
WORKBENCH_PassList *psl = vedata->psl;
WORKBENCH_EffectInfo *effect_info = stl->effects;
const DRWContextState *draw_ctx = DRW_context_state_get();
if (draw_ctx->evil_C != NULL) {
struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C);
wpd->is_playback = ED_screen_animation_playing(wm) != NULL;
}
else {
wpd->is_playback = false;
}
if (workbench_is_taa_enabled(wpd)) {
psl->effect_aa_pass = workbench_taa_create_pass(vedata, tx);
}
else if (workbench_is_fxaa_enabled(wpd)) {
psl->effect_aa_pass = workbench_fxaa_create_pass(tx);
effect_info->jitter_index = 0;
}
else {
psl->effect_aa_pass = NULL;
effect_info->jitter_index = 0;
}
}
static void workspace_aa_draw_transform(GPUTexture *tx, WORKBENCH_PrivateData *UNUSED(wpd))
{
DRW_transform_none(tx);
}
void workbench_aa_draw_pass(WORKBENCH_Data *vedata, GPUTexture *tx)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PrivateData *wpd = stl->g_data;
WORKBENCH_FramebufferList *fbl = vedata->fbl;
WORKBENCH_PassList *psl = vedata->psl;
WORKBENCH_EffectInfo *effect_info = stl->effects;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
if (workbench_is_fxaa_enabled(wpd)) {
GPU_framebuffer_bind(fbl->effect_fb);
workspace_aa_draw_transform(tx, wpd);
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_draw_pass(psl->effect_aa_pass);
}
else if (workbench_is_taa_enabled(wpd)) {
/*
* when drawing the first TAA frame, we transform directly to the
* color_only_fb as the TAA shader is just performing a direct copy.
* the workbench_taa_draw_screen_end will fill the history buffer
* for the other iterations.
*/
if (effect_info->jitter_index == 1) {
GPU_framebuffer_bind(dfbl->color_only_fb);
workspace_aa_draw_transform(tx, wpd);
}
else {
GPU_framebuffer_bind(fbl->effect_fb);
workspace_aa_draw_transform(tx, wpd);
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_draw_pass(psl->effect_aa_pass);
}
workbench_taa_draw_scene_end(vedata);
}
else {
GPU_framebuffer_bind(dfbl->color_only_fb);
workspace_aa_draw_transform(tx, wpd);
}
}

View File

@@ -0,0 +1,421 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2020, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Anti-aliasing:
*
* We use SMAA (Smart Morphological Anti-Aliasing) as a fast antialiasing solution.
*
* If the viewport stays static, the engine ask for multiple redraw and will progressively
* converge to a much more accurate image without aliasing.
* We call this one TAA (Temporal Anti-Aliasing).
*
* This is done using an accumulation buffer and a final pass that will output the final color
* to the scene buffer. We softly blend between SMAA and TAA to avoid really harsh transitions.
*/
#include "ED_screen.h"
#include "BLI_jitter_2d.h"
#include "smaa_textures.h"
#include "workbench_private.h"
static struct {
bool init;
float jitter_5[5][2];
float jitter_8[8][2];
float jitter_11[11][2];
float jitter_16[16][2];
float jitter_32[32][2];
} e_data = {false};
static void workbench_taa_jitter_init_order(float (*table)[2], int num)
{
BLI_jitter_init(table, num);
/* find closest element to center */
int closest_index = 0;
float closest_squared_distance = 1.0f;
for (int index = 0; index < num; index++) {
const float squared_dist = square_f(table[index][0]) + square_f(table[index][1]);
if (squared_dist < closest_squared_distance) {
closest_squared_distance = squared_dist;
closest_index = index;
}
}
/* move jitter table so that closest sample is in center */
for (int index = 0; index < num; index++) {
sub_v2_v2(table[index], table[closest_index]);
mul_v2_fl(table[index], 2.0f);
}
/* swap center sample to the start of the table */
if (closest_index != 0) {
swap_v2_v2(table[0], table[closest_index]);
}
/* sort list based on furtest distance with previous */
for (int i = 0; i < num - 2; i++) {
float f_squared_dist = 0.0;
int f_index = i;
for (int j = i + 1; j < num; j++) {
const float squared_dist = square_f(table[i][0] - table[j][0]) +
square_f(table[i][1] - table[j][1]);
if (squared_dist > f_squared_dist) {
f_squared_dist = squared_dist;
f_index = j;
}
}
swap_v2_v2(table[i + 1], table[f_index]);
}
}
static void workbench_taa_jitter_init(void)
{
if (e_data.init == false) {
e_data.init = true;
workbench_taa_jitter_init_order(e_data.jitter_5, 5);
workbench_taa_jitter_init_order(e_data.jitter_8, 8);
workbench_taa_jitter_init_order(e_data.jitter_11, 11);
workbench_taa_jitter_init_order(e_data.jitter_16, 16);
workbench_taa_jitter_init_order(e_data.jitter_32, 32);
}
}
int workbench_antialiasing_sample_count_get(WORKBENCH_PrivateData *wpd)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
if (wpd->is_navigating || wpd->is_playback) {
/* Only draw using SMAA or no AA when navigating. */
return min_ii(wpd->preferences->viewport_aa, 1);
}
else if (DRW_state_is_image_render()) {
if (draw_ctx->v3d) {
return scene->display.viewport_aa;
}
else {
return scene->display.render_aa;
}
}
else {
return wpd->preferences->viewport_aa;
}
}
void workbench_antialiasing_view_updated(WORKBENCH_Data *vedata)
{
WORKBENCH_StorageList *stl = vedata->stl;
if (stl && stl->wpd) {
stl->wpd->view_updated = true;
}
}
void workbench_antialiasing_engine_init(WORKBENCH_Data *vedata)
{
WORKBENCH_FramebufferList *fbl = vedata->fbl;
WORKBENCH_TextureList *txl = vedata->txl;
WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
DrawEngineType *owner = (DrawEngineType *)&workbench_antialiasing_engine_init;
wpd->view = NULL;
/* reset complete drawing when navigating. */
if (wpd->taa_sample != 0) {
if (wpd->is_navigating) {
wpd->taa_sample = 0;
}
}
if (wpd->view_updated) {
wpd->taa_sample = 0;
wpd->view_updated = false;
}
{
float persmat[4][4];
DRW_view_persmat_get(NULL, persmat, false);
if (!equals_m4m4(persmat, wpd->last_mat)) {
copy_m4_m4(wpd->last_mat, persmat);
wpd->taa_sample = 0;
}
}
if (wpd->taa_sample_len > 0) {
workbench_taa_jitter_init();
DRW_texture_ensure_fullscreen_2d(&txl->history_buffer_tx, GPU_RGBA16F, DRW_TEX_FILTER);
DRW_texture_ensure_fullscreen_2d(&txl->depth_buffer_tx, GPU_DEPTH24_STENCIL8, 0);
wpd->smaa_edge_tx = DRW_texture_pool_query_fullscreen(GPU_RG8, owner);
wpd->smaa_weight_tx = DRW_texture_pool_query_fullscreen(GPU_RGBA8, owner);
GPU_framebuffer_ensure_config(&fbl->antialiasing_fb,
{
GPU_ATTACHMENT_TEXTURE(txl->depth_buffer_tx),
GPU_ATTACHMENT_TEXTURE(txl->history_buffer_tx),
});
GPU_framebuffer_ensure_config(&fbl->smaa_edge_fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(wpd->smaa_edge_tx),
});
GPU_framebuffer_ensure_config(&fbl->smaa_weight_fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(wpd->smaa_weight_tx),
});
/* TODO could be shared for all viewports. */
if (txl->smaa_search_tx == NULL) {
txl->smaa_search_tx = GPU_texture_create_nD(SEARCHTEX_WIDTH,
SEARCHTEX_HEIGHT,
0,
2,
searchTexBytes,
GPU_R8,
GPU_DATA_UNSIGNED_BYTE,
0,
false,
NULL);
txl->smaa_area_tx = GPU_texture_create_nD(AREATEX_WIDTH,
AREATEX_HEIGHT,
0,
2,
areaTexBytes,
GPU_RG8,
GPU_DATA_UNSIGNED_BYTE,
0,
false,
NULL);
GPU_texture_bind(txl->smaa_search_tx, 0);
GPU_texture_filter_mode(txl->smaa_search_tx, true);
GPU_texture_unbind(txl->smaa_search_tx);
GPU_texture_bind(txl->smaa_area_tx, 0);
GPU_texture_filter_mode(txl->smaa_area_tx, true);
GPU_texture_unbind(txl->smaa_area_tx);
}
}
else {
/* Cleanup */
DRW_TEXTURE_FREE_SAFE(txl->history_buffer_tx);
DRW_TEXTURE_FREE_SAFE(txl->depth_buffer_tx);
DRW_TEXTURE_FREE_SAFE(txl->smaa_search_tx);
DRW_TEXTURE_FREE_SAFE(txl->smaa_area_tx);
}
}
void workbench_antialiasing_cache_init(WORKBENCH_Data *vedata)
{
WORKBENCH_TextureList *txl = vedata->txl;
WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
WORKBENCH_PassList *psl = vedata->psl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DRWShadingGroup *grp = NULL;
if (wpd->taa_sample_len == 0) {
return;
}
{
DRW_PASS_CREATE(psl->aa_accum_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
GPUShader *shader = workbench_shader_antialiasing_accumulation_get();
grp = DRW_shgroup_create(shader, psl->aa_accum_ps);
DRW_shgroup_uniform_texture(grp, "colorBuffer", dtxl->color);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
const float *size = DRW_viewport_size_get();
const float *sizeinv = DRW_viewport_invert_size_get();
float metrics[4] = {sizeinv[0], sizeinv[1], size[0], size[1]};
{
/* Stage 1: Edge detection. */
DRW_PASS_CREATE(psl->aa_edge_ps, DRW_STATE_WRITE_COLOR);
GPUShader *sh = workbench_shader_antialiasing_get(0);
grp = DRW_shgroup_create(sh, psl->aa_edge_ps);
DRW_shgroup_uniform_texture(grp, "colorTex", txl->history_buffer_tx);
DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics);
DRW_shgroup_clear_framebuffer(grp, GPU_COLOR_BIT, 0, 0, 0, 0, 0.0f, 0x0);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
{
/* Stage 2: Blend Weight/Coord. */
DRW_PASS_CREATE(psl->aa_weight_ps, DRW_STATE_WRITE_COLOR);
GPUShader *sh = workbench_shader_antialiasing_get(1);
grp = DRW_shgroup_create(sh, psl->aa_weight_ps);
DRW_shgroup_uniform_texture(grp, "edgesTex", wpd->smaa_edge_tx);
DRW_shgroup_uniform_texture(grp, "areaTex", txl->smaa_area_tx);
DRW_shgroup_uniform_texture(grp, "searchTex", txl->smaa_search_tx);
DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics);
DRW_shgroup_clear_framebuffer(grp, GPU_COLOR_BIT, 0, 0, 0, 0, 0.0f, 0x0);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
{
/* Stage 3: Resolve. */
DRW_PASS_CREATE(psl->aa_resolve_ps, DRW_STATE_WRITE_COLOR);
GPUShader *sh = workbench_shader_antialiasing_get(2);
grp = DRW_shgroup_create(sh, psl->aa_resolve_ps);
DRW_shgroup_uniform_texture(grp, "blendTex", wpd->smaa_weight_tx);
DRW_shgroup_uniform_texture(grp, "colorTex", txl->history_buffer_tx);
DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics);
DRW_shgroup_uniform_float(grp, "mixFactor", &wpd->smaa_mix_factor, 1);
DRW_shgroup_uniform_float(grp, "taaSampleCountInv", &wpd->taa_sample_inv, 1);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}
/* Return true if render is not cached. */
bool workbench_antialiasing_setup(WORKBENCH_Data *vedata)
{
WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
if (wpd->taa_sample_len == 0) {
/* AA disabled. */
return true;
}
if (wpd->taa_sample >= wpd->taa_sample_len) {
/* TAA accumulation has finish. Just copy the result back */
return false;
}
else {
const float *viewport_size = DRW_viewport_size_get();
const DRWView *default_view = DRW_view_default_get();
float *transform_offset;
switch (wpd->taa_sample_len) {
default:
case 5:
transform_offset = e_data.jitter_5[min_ii(wpd->taa_sample, 5)];
break;
case 8:
transform_offset = e_data.jitter_8[min_ii(wpd->taa_sample, 8)];
break;
case 11:
transform_offset = e_data.jitter_11[min_ii(wpd->taa_sample, 11)];
break;
case 16:
transform_offset = e_data.jitter_16[min_ii(wpd->taa_sample, 16)];
break;
case 32:
transform_offset = e_data.jitter_32[min_ii(wpd->taa_sample, 32)];
break;
}
/* construct new matrices from transform delta */
float winmat[4][4], viewmat[4][4], persmat[4][4];
DRW_view_winmat_get(default_view, winmat, false);
DRW_view_viewmat_get(default_view, viewmat, false);
DRW_view_persmat_get(default_view, persmat, false);
window_translate_m4(winmat,
persmat,
transform_offset[0] / viewport_size[0],
transform_offset[1] / viewport_size[1]);
if (wpd->view) {
/* When rendering just update the view. This avoids recomputing the culling. */
DRW_view_update_sub(wpd->view, viewmat, winmat);
}
else {
/* TAA is not making a big change to the matrices.
* Reuse the main view culling by creating a subview. */
wpd->view = DRW_view_create_sub(default_view, viewmat, winmat);
}
DRW_view_set_active(wpd->view);
return true;
}
}
void workbench_antialiasing_draw_pass(WORKBENCH_Data *vedata)
{
WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
WORKBENCH_FramebufferList *fbl = vedata->fbl;
WORKBENCH_PassList *psl = vedata->psl;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
if (wpd->taa_sample_len == 0) {
/* AA disabled. */
/* Just set sample to 1 to avoid rendering indefinitely. */
wpd->taa_sample = 1;
return;
}
/**
* We always do SMAA on top of TAA accumulation, unless the number of samples of TAA is already
* high. This ensure a smoother transition.
* If TAA accumulation is finished, we only blit the result.
*/
if (wpd->taa_sample == 0) {
/* In playback mode, we are sure the next redraw will not use the same viewmatrix.
* In this case no need to save the depth buffer. */
eGPUFrameBufferBits bits = GPU_COLOR_BIT | (!wpd->is_playback ? GPU_DEPTH_BIT : 0);
GPU_framebuffer_blit(dfbl->default_fb, 0, fbl->antialiasing_fb, 0, bits);
}
else {
/* Accumulate result to the TAA buffer. */
GPU_framebuffer_bind(fbl->antialiasing_fb);
DRW_draw_pass(psl->aa_accum_ps);
/* Copy back the saved depth buffer for correct overlays. */
GPU_framebuffer_blit(fbl->antialiasing_fb, 0, dfbl->default_fb, 0, GPU_DEPTH_BIT);
}
if (!DRW_state_is_image_render() || wpd->taa_sample + 1 == wpd->taa_sample_len) {
/* After a certain point SMAA is no longer necessary. */
wpd->smaa_mix_factor = 1.0f - clamp_f(wpd->taa_sample / 4.0f, 0.0f, 1.0f);
wpd->taa_sample_inv = 1.0f / (wpd->taa_sample + 1);
if (wpd->smaa_mix_factor > 0.0f) {
GPU_framebuffer_bind(fbl->smaa_edge_fb);
DRW_draw_pass(psl->aa_edge_ps);
GPU_framebuffer_bind(fbl->smaa_weight_fb);
DRW_draw_pass(psl->aa_weight_ps);
}
GPU_framebuffer_bind(dfbl->default_fb);
DRW_draw_pass(psl->aa_resolve_ps);
}
wpd->taa_sample++;
if (!DRW_state_is_image_render() && wpd->taa_sample < wpd->taa_sample_len) {
DRW_viewport_request_redraw();
}
}

View File

@@ -0,0 +1,182 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2020, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Cavity Effect:
*
* We use Screen Space Ambient Occlusion (SSAO) to enhance geometric details of the surfaces.
* We also use a Curvature effect computed only using the surface normals.
*
* This is done after the opaque pass. It only affects the opaque surfaces.
*/
#include "DRW_render.h"
#include "BLI_rand.h"
#include "../eevee/eevee_lut.h" /* TODO find somewhere to share blue noise Table */
#include "workbench_engine.h"
#include "workbench_private.h"
#define JITTER_TEX_SIZE 64
#define CAVITY_MAX_SAMPLES 512
/* Using Hammersley distribution */
static float *create_disk_samples(int num_samples, int num_iterations)
{
const int total_samples = num_samples * num_iterations;
const float num_samples_inv = 1.0f / num_samples;
/* vec4 to ensure memory alignment. */
float(*texels)[4] = MEM_callocN(sizeof(float[4]) * CAVITY_MAX_SAMPLES, __func__);
for (int i = 0; i < total_samples; i++) {
float it_add = (i / num_samples) * 0.499f;
float r = fmodf((i + 0.5f + it_add) * num_samples_inv, 1.0f);
double dphi;
BLI_hammersley_1d(i, &dphi);
float phi = (float)dphi * 2.0f * M_PI + it_add;
texels[i][0] = cosf(phi);
texels[i][1] = sinf(phi);
/* This deliberately distribute more samples
* at the center of the disk (and thus the shadow). */
texels[i][2] = r;
}
return (float *)texels;
}
static struct GPUTexture *create_jitter_texture(int num_samples)
{
float jitter[64 * 64][4];
const float num_samples_inv = 1.0f / num_samples;
for (int i = 0; i < 64 * 64; i++) {
float phi = blue_noise[i][0] * 2.0f * M_PI;
/* This rotate the sample per pixels */
jitter[i][0] = cosf(phi);
jitter[i][1] = sinf(phi);
/* This offset the sample along it's direction axis (reduce banding) */
float bn = blue_noise[i][1] - 0.5f;
CLAMP(bn, -0.499f, 0.499f); /* fix fireflies */
jitter[i][2] = bn * num_samples_inv;
jitter[i][3] = blue_noise[i][1];
}
UNUSED_VARS(bsdf_split_sum_ggx, btdf_split_sum_ggx, ltc_mag_ggx, ltc_mat_ggx, ltc_disk_integral);
return DRW_texture_create_2d(64, 64, GPU_RGBA16F, DRW_TEX_WRAP, &jitter[0][0]);
}
BLI_INLINE int workbench_cavity_total_sample_count(const WORKBENCH_PrivateData *wpd,
const Scene *scene)
{
return min_ii(max_ii(1, wpd->taa_sample_len) * scene->display.matcap_ssao_samples,
CAVITY_MAX_SAMPLES);
}
void workbench_cavity_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd)
{
View3DShading *shading = &wpd->shading;
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
if (CAVITY_ENABLED(wpd)) {
int cavity_sample_count_single_iteration = scene->display.matcap_ssao_samples;
int cavity_sample_count_total = workbench_cavity_total_sample_count(wpd, scene);
int max_iter_count = cavity_sample_count_total / cavity_sample_count_single_iteration;
int sample = wpd->taa_sample % max_iter_count;
wd->cavity_sample_start = cavity_sample_count_single_iteration * sample;
wd->cavity_sample_end = cavity_sample_count_single_iteration * (sample + 1);
wd->cavity_sample_count_inv = 1.0f / (wd->cavity_sample_end - wd->cavity_sample_start);
wd->cavity_jitter_scale = 1.0f / 64.0f;
wd->cavity_valley_factor = shading->cavity_valley_factor;
wd->cavity_ridge_factor = shading->cavity_ridge_factor;
wd->cavity_attenuation = scene->display.matcap_ssao_attenuation;
wd->cavity_distance = scene->display.matcap_ssao_distance;
wd->curvature_ridge = 0.5f / max_ff(square_f(shading->curvature_ridge_factor), 1e-4f);
wd->curvature_valley = 0.7f / max_ff(square_f(shading->curvature_valley_factor), 1e-4f);
}
}
void workbench_cavity_samples_ubo_ensure(WORKBENCH_PrivateData *wpd)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
int cavity_sample_count_single_iteration = scene->display.matcap_ssao_samples;
int cavity_sample_count = workbench_cavity_total_sample_count(wpd, scene);
if (wpd->vldata->cavity_sample_count != cavity_sample_count) {
DRW_UBO_FREE_SAFE(wpd->vldata->cavity_sample_ubo);
DRW_TEXTURE_FREE_SAFE(wpd->vldata->cavity_jitter_tx);
}
if (wpd->vldata->cavity_sample_ubo == NULL) {
float *samples = create_disk_samples(cavity_sample_count_single_iteration,
max_ii(1, wpd->taa_sample_len));
wpd->vldata->cavity_jitter_tx = create_jitter_texture(cavity_sample_count);
/* NOTE: Uniform buffer needs to always be filled to be valid. */
wpd->vldata->cavity_sample_ubo = DRW_uniformbuffer_create(
sizeof(float[4]) * CAVITY_MAX_SAMPLES, samples);
wpd->vldata->cavity_sample_count = cavity_sample_count;
MEM_freeN(samples);
}
}
void workbench_cavity_cache_init(WORKBENCH_Data *data)
{
WORKBENCH_PassList *psl = data->psl;
WORKBENCH_PrivateData *wpd = data->stl->wpd;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
struct GPUShader *sh;
DRWShadingGroup *grp;
if (CAVITY_ENABLED(wpd)) {
workbench_cavity_samples_ubo_ensure(wpd);
int state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL;
DRW_PASS_CREATE(psl->cavity_ps, state);
sh = workbench_shader_cavity_get(SSAO_ENABLED(wpd), CURVATURE_ENABLED(wpd));
grp = DRW_shgroup_create(sh, psl->cavity_ps);
DRW_shgroup_uniform_texture(grp, "normalBuffer", wpd->normal_buffer_tx);
DRW_shgroup_uniform_block(grp, "samples_block", wpd->vldata->cavity_sample_ubo);
DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
if (SSAO_ENABLED(wpd)) {
DRW_shgroup_uniform_texture(grp, "depthBuffer", dtxl->depth);
DRW_shgroup_uniform_texture(grp, "cavityJitter", wpd->vldata->cavity_jitter_tx);
}
if (CURVATURE_ENABLED(wpd)) {
DRW_shgroup_uniform_texture(grp, "objectIdBuffer", wpd->object_id_tx);
}
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
else {
psl->cavity_ps = NULL;
}
}

View File

@@ -18,6 +18,17 @@
/** \file
* \ingroup draw_engine
*
* Depth of Field Effect:
*
* We use a gather approach by sampling a lowres version of the color buffer.
* The process can be summarized like this:
* - downsample the color buffer using a COC (Circle of Confusion) aware downsample algo.
* - do a gather pass using the COC computed in the previous pass.
* - do a median filter to reduce noise amount.
* - composite on top of main color buffer.
*
* This is done after all passes and affects every surfaces.
*/
#include "workbench_private.h"
@@ -27,24 +38,6 @@
#include "DNA_camera_types.h"
/* *********** STATIC *********** */
static struct {
struct GPUShader *effect_dof_prepare_sh;
struct GPUShader *effect_dof_downsample_sh;
struct GPUShader *effect_dof_flatten_v_sh;
struct GPUShader *effect_dof_flatten_h_sh;
struct GPUShader *effect_dof_dilate_v_sh;
struct GPUShader *effect_dof_dilate_h_sh;
struct GPUShader *effect_dof_blur1_sh;
struct GPUShader *effect_dof_blur2_sh;
struct GPUShader *effect_dof_resolve_sh;
} e_data = {NULL};
/* Shaders */
extern char datatoc_workbench_effect_dof_frag_glsl[];
/* *********** Functions *********** */
/**
* Transform [-1..1] square to unit circle.
*/
@@ -130,52 +123,40 @@ static void workbench_dof_setup_samples(struct GPUUniformBuffer **ubo,
DRW_uniformbuffer_update(*ubo, *data);
}
void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera)
void workbench_dof_engine_init(WORKBENCH_Data *vedata)
{
WORKBENCH_TextureList *txl = vedata->txl;
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PrivateData *wpd = stl->g_data;
WORKBENCH_PrivateData *wpd = stl->wpd;
WORKBENCH_FramebufferList *fbl = vedata->fbl;
const DRWContextState *draw_ctx = DRW_context_state_get();
RegionView3D *rv3d = draw_ctx->rv3d;
View3D *v3d = draw_ctx->v3d;
Scene *scene = draw_ctx->scene;
Object *camera;
if (v3d && rv3d) {
camera = (rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL;
}
else {
camera = scene->camera;
}
Camera *cam = camera != NULL ? camera->data : NULL;
if ((wpd->shading.flag & V3D_SHADING_DEPTH_OF_FIELD) == 0 || (cam == NULL) ||
((cam->dof.flag & CAM_DOF_ENABLED) == 0)) {
wpd->dof_enabled = false;
/* Cleanup. */
DRW_TEXTURE_FREE_SAFE(txl->dof_source_tx);
DRW_TEXTURE_FREE_SAFE(txl->coc_halfres_tx);
return;
}
if (e_data.effect_dof_prepare_sh == NULL) {
e_data.effect_dof_prepare_sh = DRW_shader_create_fullscreen(
datatoc_workbench_effect_dof_frag_glsl, "#define PREPARE\n");
e_data.effect_dof_downsample_sh = DRW_shader_create_fullscreen(
datatoc_workbench_effect_dof_frag_glsl, "#define DOWNSAMPLE\n");
e_data.effect_dof_flatten_v_sh = DRW_shader_create_fullscreen(
datatoc_workbench_effect_dof_frag_glsl, "#define FLATTEN_VERTICAL\n");
e_data.effect_dof_flatten_h_sh = DRW_shader_create_fullscreen(
datatoc_workbench_effect_dof_frag_glsl, "#define FLATTEN_HORIZONTAL\n");
e_data.effect_dof_dilate_v_sh = DRW_shader_create_fullscreen(
datatoc_workbench_effect_dof_frag_glsl, "#define DILATE_VERTICAL\n");
e_data.effect_dof_dilate_h_sh = DRW_shader_create_fullscreen(
datatoc_workbench_effect_dof_frag_glsl, "#define DILATE_HORIZONTAL\n");
e_data.effect_dof_blur1_sh = DRW_shader_create_fullscreen(
datatoc_workbench_effect_dof_frag_glsl, "#define BLUR1\n");
e_data.effect_dof_blur2_sh = DRW_shader_create_fullscreen(
datatoc_workbench_effect_dof_frag_glsl, "#define BLUR2\n");
e_data.effect_dof_resolve_sh = DRW_shader_create_fullscreen(
datatoc_workbench_effect_dof_frag_glsl, "#define RESOLVE\n");
}
const float *full_size = DRW_viewport_size_get();
int size[2] = {max_ii(1, (int)full_size[0] / 2), max_ii(1, (int)full_size[1] / 2)};
#if 0
#if 0 /* TODO(fclem) finish COC min_max optimisation */
/* NOTE: We Ceil here in order to not miss any edge texel if using a NPO2 texture. */
int shrink_h_size[2] = {ceilf(size[0] / 8.0f), size[1]};
int shrink_w_size[2] = {shrink_h_size[0], ceilf(size[1] / 8.0f)};
@@ -186,14 +167,14 @@ void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera)
DRW_texture_ensure_2d(
&txl->coc_halfres_tx, size[0], size[1], GPU_RG8, DRW_TEX_FILTER | DRW_TEX_MIPMAP);
wpd->dof_blur_tx = DRW_texture_pool_query_2d(
size[0], size[1], GPU_RGBA16F, &draw_engine_workbench_solid);
#if 0
size[0], size[1], GPU_RGBA16F, &draw_engine_workbench);
#if 0 /* TODO(fclem) finish COC min_max optimisation */
wpd->coc_temp_tx = DRW_texture_pool_query_2d(
shrink_h_size[0], shrink_h_size[1], GPU_RG8, &draw_engine_workbench_solid);
shrink_h_size[0], shrink_h_size[1], GPU_RG8, &draw_engine_workbench);
wpd->coc_tiles_tx[0] = DRW_texture_pool_query_2d(
shrink_w_size[0], shrink_w_size[1], GPU_RG8, &draw_engine_workbench_solid);
shrink_w_size[0], shrink_w_size[1], GPU_RG8, &draw_engine_workbench);
wpd->coc_tiles_tx[1] = DRW_texture_pool_query_2d(
shrink_w_size[0], shrink_w_size[1], GPU_RG8, &draw_engine_workbench_solid);
shrink_w_size[0], shrink_w_size[1], GPU_RG8, &draw_engine_workbench);
#endif
GPU_framebuffer_ensure_config(&fbl->dof_downsample_fb,
@@ -202,7 +183,7 @@ void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera)
GPU_ATTACHMENT_TEXTURE(txl->dof_source_tx),
GPU_ATTACHMENT_TEXTURE(txl->coc_halfres_tx),
});
#if 0
#if 0 /* TODO(fclem) finish COC min_max optimisation */
GPU_framebuffer_ensure_config(&fbl->dof_coc_tile_h_fb,
{
GPU_ATTACHMENT_NONE,
@@ -231,11 +212,7 @@ void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera)
});
{
const DRWContextState *draw_ctx = DRW_context_state_get();
RegionView3D *rv3d = draw_ctx->rv3d;
/* Parameters */
/* TODO UI Options */
float fstop = cam->dof.aperture_fstop;
float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
float focus_dist = BKE_camera_object_dof_distance(camera);
@@ -263,128 +240,125 @@ void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera)
float rotation = cam->dof.aperture_rotation;
float ratio = 1.0f / cam->dof.aperture_ratio;
if (wpd->dof_ubo == NULL || blades != wpd->dof_blades || rotation != wpd->dof_rotation ||
ratio != wpd->dof_ratio) {
if (wpd->vldata->dof_sample_ubo == NULL || blades != wpd->dof_blades ||
rotation != wpd->dof_rotation || ratio != wpd->dof_ratio) {
wpd->dof_blades = blades;
wpd->dof_rotation = rotation;
wpd->dof_ratio = ratio;
workbench_dof_setup_samples(&wpd->dof_ubo, &stl->dof_ubo_data, blades, rotation, ratio);
workbench_dof_setup_samples(
&wpd->vldata->dof_sample_ubo, &stl->dof_ubo_data, blades, rotation, ratio);
}
}
wpd->dof_enabled = true;
}
void workbench_dof_create_pass(WORKBENCH_Data *vedata,
GPUTexture **dof_input,
GPUTexture *noise_tex)
void workbench_dof_cache_init(WORKBENCH_Data *vedata)
{
WORKBENCH_PassList *psl = vedata->psl;
WORKBENCH_TextureList *txl = vedata->txl;
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PrivateData *wpd = stl->g_data;
struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
WORKBENCH_PrivateData *wpd = stl->wpd;
if (!wpd->dof_enabled) {
return;
}
GPUShader *prepare_sh, *downsample_sh, *blur1_sh, *blur2_sh, *resolve_sh;
workbench_shader_depth_of_field_get(
&prepare_sh, &downsample_sh, &blur1_sh, &blur2_sh, &resolve_sh);
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
psl->dof_down_ps = DRW_pass_create("DoF DownSample", DRW_STATE_WRITE_COLOR);
psl->dof_down2_ps = DRW_pass_create("DoF DownSample", DRW_STATE_WRITE_COLOR);
psl->dof_flatten_h_ps = DRW_pass_create("DoF Flatten Coc H", DRW_STATE_WRITE_COLOR);
psl->dof_flatten_v_ps = DRW_pass_create("DoF Flatten Coc V", DRW_STATE_WRITE_COLOR);
psl->dof_dilate_h_ps = DRW_pass_create("DoF Dilate Coc H", DRW_STATE_WRITE_COLOR);
psl->dof_dilate_v_ps = DRW_pass_create("DoF Dilate Coc V", DRW_STATE_WRITE_COLOR);
psl->dof_blur1_ps = DRW_pass_create("DoF Blur 1", DRW_STATE_WRITE_COLOR);
psl->dof_blur2_ps = DRW_pass_create("DoF Blur 2", DRW_STATE_WRITE_COLOR);
psl->dof_resolve_ps = DRW_pass_create("DoF Resolve",
DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
{
DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_prepare_sh, psl->dof_down_ps);
DRW_shgroup_uniform_texture_ref(grp, "sceneColorTex", dof_input);
psl->dof_down_ps = DRW_pass_create("DoF DownSample", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(prepare_sh, psl->dof_down_ps);
DRW_shgroup_uniform_texture(grp, "sceneColorTex", dtxl->color);
DRW_shgroup_uniform_texture(grp, "sceneDepthTex", dtxl->depth);
DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
DRW_shgroup_uniform_vec3(grp, "dofParams", &wpd->dof_aperturesize, 1);
DRW_shgroup_uniform_vec2(grp, "nearFar", wpd->dof_near_far, 1);
DRW_shgroup_call(grp, quad, NULL);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
{
DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_downsample_sh, psl->dof_down2_ps);
psl->dof_down2_ps = DRW_pass_create("DoF DownSample", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(downsample_sh, psl->dof_down2_ps);
DRW_shgroup_uniform_texture(grp, "sceneColorTex", txl->dof_source_tx);
DRW_shgroup_uniform_texture(grp, "inputCocTex", txl->coc_halfres_tx);
DRW_shgroup_call(grp, quad, NULL);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
#if 0
#if 0 /* TODO(fclem) finish COC min_max optimization */
{
DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_flatten_h_sh,
psl->dof_flatten_h_ps);
psl->dof_flatten_h_ps = DRW_pass_create("DoF Flatten Coc H", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(flatten_h_sh, psl->dof_flatten_h_ps);
DRW_shgroup_uniform_texture(grp, "inputCocTex", txl->coc_halfres_tx);
DRW_shgroup_call(grp, quad, NULL);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
{
DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_flatten_v_sh,
psl->dof_flatten_v_ps);
psl->dof_flatten_v_ps = DRW_pass_create("DoF Flatten Coc V", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(flatten_v_sh, psl->dof_flatten_v_ps);
DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_temp_tx);
DRW_shgroup_call(grp, quad, NULL);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
{
DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_dilate_v_sh, psl->dof_dilate_v_ps);
psl->dof_dilate_h_ps = DRW_pass_create("DoF Dilate Coc H", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(dilate_v_sh, psl->dof_dilate_v_ps);
DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_tiles_tx[0]);
DRW_shgroup_call(grp, quad, NULL);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
{
DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_dilate_h_sh, psl->dof_dilate_h_ps);
psl->dof_dilate_v_ps = DRW_pass_create("DoF Dilate Coc V", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(dilate_h_sh, psl->dof_dilate_h_ps);
DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_tiles_tx[1]);
DRW_shgroup_call(grp, quad, NULL);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
#endif
{
float offset = stl->effects->jitter_index /
(float)workbench_taa_calculate_num_iterations(vedata);
DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_blur1_sh, psl->dof_blur1_ps);
DRW_shgroup_uniform_block(grp, "dofSamplesBlock", wpd->dof_ubo);
DRW_shgroup_uniform_texture(grp, "noiseTex", noise_tex);
psl->dof_blur1_ps = DRW_pass_create("DoF Blur 1", DRW_STATE_WRITE_COLOR);
/* We reuse the same noise texture. Ensure it is up to date. */
workbench_cavity_samples_ubo_ensure(wpd);
float offset = wpd->taa_sample / wpd->taa_sample_len;
DRWShadingGroup *grp = DRW_shgroup_create(blur1_sh, psl->dof_blur1_ps);
DRW_shgroup_uniform_block(grp, "dofSamplesBlock", wpd->vldata->dof_sample_ubo);
DRW_shgroup_uniform_texture(grp, "noiseTex", wpd->vldata->cavity_jitter_tx);
DRW_shgroup_uniform_texture(grp, "inputCocTex", txl->coc_halfres_tx);
DRW_shgroup_uniform_texture(grp, "halfResColorTex", txl->dof_source_tx);
DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
DRW_shgroup_uniform_float_copy(grp, "noiseOffset", offset);
DRW_shgroup_call(grp, quad, NULL);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
{
DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_blur2_sh, psl->dof_blur2_ps);
psl->dof_blur2_ps = DRW_pass_create("DoF Blur 2", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(blur2_sh, psl->dof_blur2_ps);
DRW_shgroup_uniform_texture(grp, "inputCocTex", txl->coc_halfres_tx);
DRW_shgroup_uniform_texture(grp, "blurTex", wpd->dof_blur_tx);
DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
DRW_shgroup_call(grp, quad, NULL);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
{
DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_resolve_sh, psl->dof_resolve_ps);
psl->dof_resolve_ps = DRW_pass_create("DoF Resolve",
DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
DRWShadingGroup *grp = DRW_shgroup_create(resolve_sh, psl->dof_resolve_ps);
DRW_shgroup_uniform_texture(grp, "halfResColorTex", txl->dof_source_tx);
DRW_shgroup_uniform_texture(grp, "sceneDepthTex", dtxl->depth);
DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
DRW_shgroup_uniform_vec3(grp, "dofParams", &wpd->dof_aperturesize, 1);
DRW_shgroup_uniform_vec2(grp, "nearFar", wpd->dof_near_far, 1);
DRW_shgroup_call(grp, quad, NULL);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}
void workbench_dof_engine_free(void)
{
DRW_SHADER_FREE_SAFE(e_data.effect_dof_prepare_sh);
DRW_SHADER_FREE_SAFE(e_data.effect_dof_downsample_sh);
DRW_SHADER_FREE_SAFE(e_data.effect_dof_flatten_v_sh);
DRW_SHADER_FREE_SAFE(e_data.effect_dof_flatten_h_sh);
DRW_SHADER_FREE_SAFE(e_data.effect_dof_dilate_v_sh);
DRW_SHADER_FREE_SAFE(e_data.effect_dof_dilate_h_sh);
DRW_SHADER_FREE_SAFE(e_data.effect_dof_blur1_sh);
DRW_SHADER_FREE_SAFE(e_data.effect_dof_blur2_sh);
DRW_SHADER_FREE_SAFE(e_data.effect_dof_resolve_sh);
}
static void workbench_dof_downsample_level(void *userData, int UNUSED(level))
{
WORKBENCH_PassList *psl = (WORKBENCH_PassList *)userData;
@@ -396,7 +370,8 @@ void workbench_dof_draw_pass(WORKBENCH_Data *vedata)
WORKBENCH_FramebufferList *fbl = vedata->fbl;
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PassList *psl = vedata->psl;
WORKBENCH_PrivateData *wpd = stl->g_data;
WORKBENCH_PrivateData *wpd = stl->wpd;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
if (!wpd->dof_enabled) {
return;
@@ -410,7 +385,7 @@ void workbench_dof_draw_pass(WORKBENCH_Data *vedata)
GPU_framebuffer_recursive_downsample(
fbl->dof_downsample_fb, 2, workbench_dof_downsample_level, psl);
#if 0
#if 0 /* TODO(fclem) finish COC min_max optimization */
GPU_framebuffer_bind(fbl->dof_coc_tile_h_fb);
DRW_draw_pass(psl->dof_flatten_h_ps);
@@ -430,7 +405,7 @@ void workbench_dof_draw_pass(WORKBENCH_Data *vedata)
GPU_framebuffer_bind(fbl->dof_blur2_fb);
DRW_draw_pass(psl->dof_blur2_ps);
GPU_framebuffer_bind(fbl->color_only_fb);
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_draw_pass(psl->dof_resolve_ps);
DRW_stats_group_end();

View File

@@ -1,59 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
#include "workbench_private.h"
/* *********** STATIC *********** */
static struct {
struct GPUShader *effect_fxaa_sh;
} e_data = {NULL};
/* Shaders */
extern char datatoc_common_fxaa_lib_glsl[];
extern char datatoc_common_fullscreen_vert_glsl[];
extern char datatoc_workbench_effect_fxaa_frag_glsl[];
/* *********** Functions *********** */
void workbench_fxaa_engine_init(void)
{
if (e_data.effect_fxaa_sh == NULL) {
e_data.effect_fxaa_sh = DRW_shader_create_with_lib(datatoc_common_fullscreen_vert_glsl,
NULL,
datatoc_workbench_effect_fxaa_frag_glsl,
datatoc_common_fxaa_lib_glsl,
NULL);
}
}
DRWPass *workbench_fxaa_create_pass(GPUTexture **color_buffer_tx)
{
DRWPass *pass = DRW_pass_create("Effect FXAA", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_fxaa_sh, pass);
DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", color_buffer_tx);
DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
return pass;
}
void workbench_fxaa_engine_free(void)
{
DRW_SHADER_FREE_SAFE(e_data.effect_fxaa_sh);
}

View File

@@ -0,0 +1,55 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2020, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Outline Effect:
*
* Simple effect that just samples an object id buffer to detect objects outlines.
*/
#include "DRW_render.h"
#include "workbench_engine.h"
#include "workbench_private.h"
void workbench_outline_cache_init(WORKBENCH_Data *data)
{
WORKBENCH_PassList *psl = data->psl;
WORKBENCH_PrivateData *wpd = data->stl->wpd;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
struct GPUShader *sh;
DRWShadingGroup *grp;
if (OBJECT_OUTLINE_ENABLED(wpd)) {
int state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL;
DRW_PASS_CREATE(psl->outline_ps, state);
sh = workbench_shader_outline_get();
grp = DRW_shgroup_create(sh, psl->outline_ps);
DRW_shgroup_uniform_texture(grp, "objectIdBuffer", wpd->object_id_tx);
DRW_shgroup_uniform_texture(grp, "depthBuffer", dtxl->depth);
DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
else {
psl->outline_ps = NULL;
}
}

View File

@@ -1,305 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
#include "workbench_private.h"
#include "BLI_jitter_2d.h"
static struct {
struct GPUShader *effect_taa_sh;
float jitter_5[5][2];
float jitter_8[8][2];
float jitter_11[11][2];
float jitter_16[16][2];
float jitter_32[32][2];
} e_data = {NULL};
extern char datatoc_workbench_effect_taa_frag_glsl[];
static void workbench_taa_jitter_init_order(float (*table)[2], int num)
{
BLI_jitter_init(table, num);
/* find closest element to center */
int closest_index = 0;
float closest_squared_distance = 1.0f;
for (int index = 0; index < num; index++) {
const float squared_dist = square_f(table[index][0]) + square_f(table[index][1]);
if (squared_dist < closest_squared_distance) {
closest_squared_distance = squared_dist;
closest_index = index;
}
}
/* move jitter table so that closest sample is in center */
for (int index = 0; index < num; index++) {
sub_v2_v2(table[index], table[closest_index]);
mul_v2_fl(table[index], 2.0f);
}
/* swap center sample to the start of the table */
if (closest_index != 0) {
swap_v2_v2(table[0], table[closest_index]);
}
/* sort list based on furtest distance with previous */
for (int i = 0; i < num - 2; i++) {
float f_squared_dist = 0.0;
int f_index = i;
for (int j = i + 1; j < num; j++) {
const float squared_dist = square_f(table[i][0] - table[j][0]) +
square_f(table[i][1] - table[j][1]);
if (squared_dist > f_squared_dist) {
f_squared_dist = squared_dist;
f_index = j;
}
}
swap_v2_v2(table[i + 1], table[f_index]);
}
}
static void workbench_taa_jitter_init(void)
{
workbench_taa_jitter_init_order(e_data.jitter_5, 5);
workbench_taa_jitter_init_order(e_data.jitter_8, 8);
workbench_taa_jitter_init_order(e_data.jitter_11, 11);
workbench_taa_jitter_init_order(e_data.jitter_16, 16);
workbench_taa_jitter_init_order(e_data.jitter_32, 32);
}
int workbench_taa_calculate_num_iterations(WORKBENCH_Data *vedata)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PrivateData *wpd = stl->g_data;
const Scene *scene = DRW_context_state_get()->scene;
int result;
if (workbench_is_taa_enabled(wpd)) {
if (DRW_state_is_image_render()) {
const DRWContextState *draw_ctx = DRW_context_state_get();
if (draw_ctx->v3d) {
result = scene->display.viewport_aa;
}
else {
result = scene->display.render_aa;
}
}
else {
result = wpd->preferences->viewport_aa;
}
}
else {
/* when no TAA is disabled return 1 to render a single sample
* see `workbench_render.c` */
result = 1;
}
return result;
}
int workbench_num_viewport_rendering_iterations(WORKBENCH_Data *UNUSED(vedata))
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
int result = DRW_state_is_image_render() ? scene->display.viewport_aa : 1;
result = MAX2(result, 1);
return result;
}
void workbench_taa_engine_init(WORKBENCH_Data *vedata)
{
WORKBENCH_EffectInfo *effect_info = vedata->stl->effects;
const DRWContextState *draw_ctx = DRW_context_state_get();
RegionView3D *rv3d = draw_ctx->rv3d;
if (e_data.effect_taa_sh == NULL) {
e_data.effect_taa_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_taa_frag_glsl,
NULL);
workbench_taa_jitter_init();
}
effect_info->view = NULL;
/* reset complete drawing when navigating. */
if (effect_info->jitter_index != 0) {
if (rv3d && rv3d->rflag & (RV3D_NAVIGATING | RV3D_PAINTING)) {
effect_info->jitter_index = 0;
}
}
if (effect_info->view_updated) {
effect_info->jitter_index = 0;
effect_info->view_updated = false;
}
{
float persmat[4][4];
DRW_view_persmat_get(NULL, persmat, false);
if (!equals_m4m4(persmat, effect_info->last_mat)) {
copy_m4_m4(effect_info->last_mat, persmat);
effect_info->jitter_index = 0;
}
}
}
void workbench_taa_engine_free(void)
{
DRW_SHADER_FREE_SAFE(e_data.effect_taa_sh);
}
DRWPass *workbench_taa_create_pass(WORKBENCH_Data *vedata, GPUTexture **color_buffer_tx)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_TextureList *txl = vedata->txl;
WORKBENCH_EffectInfo *effect_info = stl->effects;
WORKBENCH_FramebufferList *fbl = vedata->fbl;
const WORKBENCH_PrivateData *wpd = stl->g_data;
{
const eGPUTextureFormat hist_buffer_format = workbench_color_texture_format(wpd);
DRW_texture_ensure_fullscreen_2d(&txl->history_buffer_tx, hist_buffer_format, 0);
DRW_texture_ensure_fullscreen_2d(&txl->depth_buffer_tx, GPU_DEPTH24_STENCIL8, 0);
}
{
GPU_framebuffer_ensure_config(&fbl->effect_taa_fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(txl->history_buffer_tx),
});
GPU_framebuffer_ensure_config(&fbl->depth_buffer_fb,
{
GPU_ATTACHMENT_TEXTURE(txl->depth_buffer_tx),
});
}
DRWPass *pass = DRW_pass_create("Effect TAA", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_taa_sh, pass);
DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", color_buffer_tx);
DRW_shgroup_uniform_texture_ref(grp, "historyBuffer", &txl->history_buffer_tx);
DRW_shgroup_uniform_float(grp, "mixFactor", &effect_info->taa_mix_factor, 1);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
return pass;
}
void workbench_taa_draw_scene_start(WORKBENCH_Data *vedata)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_EffectInfo *effect_info = stl->effects;
WORKBENCH_PrivateData *wpd = stl->g_data;
const float *viewport_size = DRW_viewport_size_get();
const DRWView *default_view = DRW_view_default_get();
int num_samples = 8;
float(*samples)[2];
num_samples = workbench_taa_calculate_num_iterations(vedata);
switch (num_samples) {
default:
case 5:
samples = e_data.jitter_5;
break;
case 8:
samples = e_data.jitter_8;
break;
case 11:
samples = e_data.jitter_11;
break;
case 16:
samples = e_data.jitter_16;
break;
case 32:
samples = e_data.jitter_32;
break;
}
const int jitter_index = effect_info->jitter_index;
const float *transform_offset = samples[jitter_index];
effect_info->taa_mix_factor = 1.0f / (jitter_index + 1);
effect_info->jitter_index = (jitter_index + 1) % num_samples;
/* Copy jitter index to Cavity iteration */
wpd->ssao_params[3] = effect_info->jitter_index;
/* construct new matrices from transform delta */
float winmat[4][4], viewmat[4][4], persmat[4][4];
DRW_view_winmat_get(default_view, winmat, false);
DRW_view_viewmat_get(default_view, viewmat, false);
DRW_view_persmat_get(default_view, persmat, false);
window_translate_m4(winmat,
persmat,
transform_offset[0] / viewport_size[0],
transform_offset[1] / viewport_size[1]);
if (effect_info->view) {
/* When rendering just update the view. This avoids recomputing the culling. */
DRW_view_update_sub(effect_info->view, viewmat, winmat);
}
else {
/* TAA is not making a big change to the matrices.
* Reuse the main view culling by creating a subview. */
effect_info->view = DRW_view_create_sub(default_view, viewmat, winmat);
}
DRW_view_set_active(effect_info->view);
}
void workbench_taa_draw_scene_end(WORKBENCH_Data *vedata)
{
/*
* If first frame then the offset is 0.0 and its depth is the depth buffer to use
* for the rest of the draw engines. We store it in a persistent buffer.
*
* If it is not the first frame we copy the persistent buffer back to the
* default depth buffer
*/
const WORKBENCH_StorageList *stl = vedata->stl;
const WORKBENCH_FramebufferList *fbl = vedata->fbl;
const DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
WORKBENCH_EffectInfo *effect_info = stl->effects;
if (effect_info->jitter_index == 1) {
GPU_framebuffer_blit(dfbl->depth_only_fb, 0, fbl->depth_buffer_fb, 0, GPU_DEPTH_BIT);
}
else {
GPU_framebuffer_blit(fbl->depth_buffer_fb, 0, dfbl->depth_only_fb, 0, GPU_DEPTH_BIT);
}
GPU_framebuffer_blit(dfbl->color_only_fb, 0, fbl->effect_taa_fb, 0, GPU_COLOR_BIT);
if (!DRW_state_is_image_render()) {
DRW_view_set_active(NULL);
}
if (effect_info->jitter_index != 0 && !DRW_state_is_image_render()) {
DRW_viewport_request_redraw();
}
}
void workbench_taa_view_updated(WORKBENCH_Data *vedata)
{
WORKBENCH_StorageList *stl = vedata->stl;
if (stl) {
WORKBENCH_EffectInfo *effect_info = stl->effects;
if (effect_info) {
effect_info->view_updated = true;
}
}
}

View File

@@ -19,18 +19,557 @@
/** \file
* \ingroup draw_engine
*
* Simple engine for drawing color and/or depth.
* When we only need simple flat shaders.
* Workbench Engine:
*
* Optimized engine to draw the working viewport with solid and transparent geometry.
*/
#include "DRW_render.h"
#include "BLI_alloca.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "DNA_image_types.h"
#include "DNA_fluid_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "workbench_engine.h"
#include "workbench_private.h"
#define WORKBENCH_ENGINE "BLENDER_WORKBENCH"
/* Note: currently unused, we may want to register so we can see this when debugging the view. */
void workbench_engine_init(void *ved)
{
WORKBENCH_Data *vedata = ved;
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_TextureList *txl = vedata->txl;
workbench_shader_library_ensure();
if (!stl->wpd) {
stl->wpd = MEM_callocN(sizeof(*stl->wpd), __func__);
stl->wpd->view_updated = true;
}
WORKBENCH_PrivateData *wpd = stl->wpd;
workbench_private_data_init(wpd);
workbench_update_world_ubo(wpd);
if (txl->dummy_image_tx == NULL) {
float fpixel[4] = {1.0f, 0.0f, 1.0f, 1.0f};
txl->dummy_image_tx = DRW_texture_create_2d(1, 1, GPU_RGBA8, 0, fpixel);
}
wpd->dummy_image_tx = txl->dummy_image_tx;
if (OBJECT_ID_PASS_ENABLED(wpd)) {
wpd->object_id_tx = DRW_texture_pool_query_fullscreen(GPU_R16UI, &draw_engine_workbench);
}
else {
/* Dont free because it's a pool texture. */
wpd->object_id_tx = NULL;
}
workbench_opaque_engine_init(vedata);
workbench_transparent_engine_init(vedata);
workbench_dof_engine_init(vedata);
workbench_antialiasing_engine_init(vedata);
workbench_volume_engine_init(vedata);
}
void workbench_cache_init(void *ved)
{
WORKBENCH_Data *vedata = ved;
workbench_opaque_cache_init(vedata);
workbench_transparent_cache_init(vedata);
workbench_shadow_cache_init(vedata);
workbench_cavity_cache_init(vedata);
workbench_outline_cache_init(vedata);
workbench_dof_cache_init(vedata);
workbench_antialiasing_cache_init(vedata);
workbench_volume_cache_init(vedata);
}
/* TODO(fclem) DRW_cache_object_surface_material_get needs a refactor to allow passing NULL
* instead of gpumat_array. Avoiding all this boilerplate code. */
static struct GPUBatch **workbench_object_surface_material_get(Object *ob)
{
const int materials_len = DRW_cache_object_material_count_get(ob);
struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len);
return DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len);
}
static void workbench_cache_sculpt_populate(WORKBENCH_PrivateData *wpd,
Object *ob,
eV3DShadingColorType color_type)
{
const bool use_vcol = ELEM(color_type, V3D_SHADING_VERTEX_COLOR);
const bool use_single_drawcall = !ELEM(color_type, V3D_SHADING_MATERIAL_COLOR);
BLI_assert(wpd->shading.color_type != V3D_SHADING_TEXTURE_COLOR);
if (use_single_drawcall) {
DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, NULL);
DRW_shgroup_call_sculpt(grp, ob, false, false, use_vcol);
}
else {
const int materials_len = DRW_cache_object_material_count_get(ob);
struct DRWShadingGroup **shgrps = BLI_array_alloca(shgrps, materials_len);
for (int i = 0; i < materials_len; i++) {
shgrps[i] = workbench_material_setup(wpd, ob, i + 1, color_type, NULL);
}
DRW_shgroup_call_sculpt_with_materials(shgrps, ob, false);
}
}
static void workbench_cache_texpaint_populate(WORKBENCH_PrivateData *wpd, Object *ob)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
const ImagePaintSettings *imapaint = &scene->toolsettings->imapaint;
const bool use_single_drawcall = imapaint->mode == IMAGEPAINT_MODE_IMAGE;
if (use_single_drawcall) {
struct GPUBatch *geom = DRW_cache_mesh_surface_texpaint_single_get(ob);
if (geom) {
Image *ima = imapaint->canvas;
int interp = (imapaint->interp == IMAGEPAINT_INTERP_LINEAR) ? SHD_INTERP_LINEAR :
SHD_INTERP_CLOSEST;
DRWShadingGroup *grp = workbench_image_setup(wpd, ob, 0, ima, NULL, interp);
DRW_shgroup_call(grp, geom, ob);
}
}
else {
struct GPUBatch **geoms = DRW_cache_mesh_surface_texpaint_get(ob);
if (geoms) {
const int materials_len = DRW_cache_object_material_count_get(ob);
for (int i = 0; i < materials_len; i++) {
DRWShadingGroup *grp = workbench_image_setup(wpd, ob, i + 1, NULL, NULL, 0);
DRW_shgroup_call(grp, geoms[i], ob);
}
}
}
}
static void workbench_cache_common_populate(WORKBENCH_PrivateData *wpd,
Object *ob,
eV3DShadingColorType color_type,
bool *r_transp)
{
const bool use_tex = ELEM(color_type, V3D_SHADING_TEXTURE_COLOR);
const bool use_vcol = ELEM(color_type, V3D_SHADING_VERTEX_COLOR);
const bool use_single_drawcall = !ELEM(
color_type, V3D_SHADING_MATERIAL_COLOR, V3D_SHADING_TEXTURE_COLOR);
if (use_single_drawcall) {
struct GPUBatch *geom = (use_vcol) ? DRW_cache_mesh_surface_vertpaint_get(ob) :
DRW_cache_object_surface_get(ob);
if (geom) {
DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, r_transp);
DRW_shgroup_call(grp, geom, ob);
}
}
else {
struct GPUBatch **geoms = (use_tex) ? DRW_cache_mesh_surface_texpaint_get(ob) :
workbench_object_surface_material_get(ob);
if (geoms) {
const int materials_len = DRW_cache_object_material_count_get(ob);
for (int i = 0; i < materials_len; i++) {
DRWShadingGroup *grp = workbench_material_setup(wpd, ob, i + 1, color_type, r_transp);
DRW_shgroup_call(grp, geoms[i], ob);
}
}
}
}
static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd,
Object *ob,
ModifierData *md,
eV3DShadingColorType color_type,
bool use_texpaint_mode)
{
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
ParticleSettings *part = psys->part;
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
const ImagePaintSettings *imapaint = use_texpaint_mode ? &scene->toolsettings->imapaint : NULL;
Image *ima = (imapaint && imapaint->mode == IMAGEPAINT_MODE_IMAGE) ? imapaint->canvas : NULL;
int interp = (imapaint && imapaint->interp == IMAGEPAINT_INTERP_LINEAR) ? SHD_INTERP_LINEAR :
SHD_INTERP_CLOSEST;
DRWShadingGroup *grp = (use_texpaint_mode) ?
workbench_image_hair_setup(wpd, ob, part->omat, ima, NULL, interp) :
workbench_material_hair_setup(wpd, ob, part->omat, color_type);
DRW_shgroup_hair_create_sub(ob, psys, md, grp);
}
/* Decide what colortype to draw the object with.
* In some cases it can be overwritten by workbench_material_setup(). */
static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
Object *ob,
bool *r_sculpt_pbvh,
bool *r_texpaint_mode,
bool *r_draw_shadow)
{
eV3DShadingColorType color_type = wpd->shading.color_type;
const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL;
const DRWContextState *draw_ctx = DRW_context_state_get();
const bool is_active = (ob == draw_ctx->obact);
const bool is_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) &&
!DRW_state_is_image_render();
const bool is_render = DRW_state_is_image_render() && (draw_ctx->v3d == NULL);
const bool is_texpaint_mode = is_active && (wpd->ctx_mode == CTX_MODE_PAINT_TEXTURE);
const bool is_vertpaint_mode = is_active && (wpd->ctx_mode == CTX_MODE_PAINT_VERTEX);
if ((color_type == V3D_SHADING_TEXTURE_COLOR) && (ob->dt < OB_TEXTURE)) {
color_type = V3D_SHADING_MATERIAL_COLOR;
}
/* Disable color mode if data layer is unavailable. */
if ((color_type == V3D_SHADING_TEXTURE_COLOR) && (me == NULL || me->mloopuv == NULL)) {
color_type = V3D_SHADING_MATERIAL_COLOR;
}
if ((color_type == V3D_SHADING_VERTEX_COLOR) && (me == NULL || me->mloopcol == NULL)) {
color_type = V3D_SHADING_OBJECT_COLOR;
}
*r_sculpt_pbvh = is_sculpt_pbvh;
*r_texpaint_mode = false;
if (!is_sculpt_pbvh && !is_render) {
/* Force texture or vertex mode if object is in paint mode. */
if (is_texpaint_mode && me && me->mloopuv) {
color_type = V3D_SHADING_TEXTURE_COLOR;
*r_texpaint_mode = true;
}
else if (is_vertpaint_mode && me && me->mloopcol) {
color_type = V3D_SHADING_VERTEX_COLOR;
}
}
if (r_draw_shadow) {
*r_draw_shadow = (ob->dtx & OB_DRAW_NO_SHADOW_CAST) == 0 && SHADOW_ENABLED(wpd);
/* Currently unsupported in sculpt mode. We could revert to the slow
* method in this case but I'm not sure if it's a good idea given that
* sculpted meshes are heavy to begin with. */
if (is_sculpt_pbvh) {
*r_draw_shadow = false;
}
if (is_active && DRW_object_use_hide_faces(ob)) {
*r_draw_shadow = false;
}
}
return color_type;
}
void workbench_cache_populate(void *ved, Object *ob)
{
WORKBENCH_Data *vedata = ved;
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PrivateData *wpd = stl->wpd;
if (!DRW_object_is_renderable(ob)) {
return;
}
if (ob->type == OB_MESH && ob->modifiers.first != NULL) {
bool use_sculpt_pbvh, use_texpaint_mode;
int color_type = workbench_color_type_get(wpd, ob, &use_sculpt_pbvh, &use_texpaint_mode, NULL);
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type != eModifierType_ParticleSystem) {
continue;
}
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
continue;
}
ParticleSettings *part = psys->part;
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
if (draw_as == PART_DRAW_PATH) {
workbench_cache_hair_populate(wpd, ob, md, color_type, use_texpaint_mode);
}
}
}
if (!(ob->base_flag & BASE_FROM_DUPLI)) {
ModifierData *md = modifiers_findByType(ob, eModifierType_Fluid);
if (md && modifier_isEnabled(wpd->scene, md, eModifierMode_Realtime)) {
FluidModifierData *fmd = (FluidModifierData *)md;
if (fmd->domain && fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) {
workbench_volume_cache_populate(vedata, wpd->scene, ob, md);
return; /* Do not draw solid in this case. */
}
}
}
if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) {
return;
}
if ((ob->dt < OB_SOLID) && !DRW_state_is_scene_render()) {
return;
}
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) {
bool use_sculpt_pbvh, use_texpaint_mode, draw_shadow, has_transp_mat = false;
eV3DShadingColorType color_type = workbench_color_type_get(
wpd, ob, &use_sculpt_pbvh, &use_texpaint_mode, &draw_shadow);
if (use_sculpt_pbvh) {
workbench_cache_sculpt_populate(wpd, ob, color_type);
}
else if (use_texpaint_mode) {
workbench_cache_texpaint_populate(wpd, ob);
}
else {
workbench_cache_common_populate(wpd, ob, color_type, &has_transp_mat);
}
if (draw_shadow) {
workbench_shadow_cache_populate(vedata, ob, has_transp_mat);
}
}
}
void workbench_cache_finish(void *ved)
{
WORKBENCH_Data *vedata = ved;
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_FramebufferList *fbl = vedata->fbl;
WORKBENCH_PrivateData *wpd = stl->wpd;
/* TODO(fclem) Only do this when really needed. */
{
/* HACK we allocate the infront depth here to avoid the overhead when if is not needed. */
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DRW_texture_ensure_fullscreen_2d(&dtxl->depth_in_front, GPU_DEPTH24_STENCIL8, 0);
GPU_framebuffer_ensure_config(&dfbl->in_front_fb,
{
GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front),
GPU_ATTACHMENT_TEXTURE(dtxl->color),
});
GPU_framebuffer_ensure_config(&fbl->opaque_infront_fb,
{
GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front),
GPU_ATTACHMENT_TEXTURE(wpd->material_buffer_tx),
GPU_ATTACHMENT_TEXTURE(wpd->normal_buffer_tx),
GPU_ATTACHMENT_TEXTURE(wpd->object_id_tx),
});
GPU_framebuffer_ensure_config(&fbl->transp_accum_infront_fb,
{
GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front),
GPU_ATTACHMENT_TEXTURE(wpd->accum_buffer_tx),
GPU_ATTACHMENT_TEXTURE(wpd->reveal_buffer_tx),
});
}
if (wpd->object_id_tx) {
GPU_framebuffer_ensure_config(&fbl->id_clear_fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(wpd->object_id_tx),
});
}
else {
GPU_FRAMEBUFFER_FREE_SAFE(fbl->id_clear_fb);
}
workbench_update_material_ubos(wpd);
/* TODO don't free reuse next redraw. */
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
if (wpd->prepass[i][j][k].material_hash) {
BLI_ghash_free(wpd->prepass[i][j][k].material_hash, NULL, NULL);
wpd->prepass[i][j][k].material_hash = NULL;
}
}
}
}
}
/* Used by viewport rendering & final rendering.
* Do one render loop iteration (i.e: One TAA sample). */
void workbench_draw_sample(void *ved)
{
WORKBENCH_Data *vedata = ved;
WORKBENCH_FramebufferList *fbl = vedata->fbl;
WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
WORKBENCH_PassList *psl = vedata->psl;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float clear_col_with_alpha[4] = {0.0f, 0.0f, 0.0f, 1.0f};
const bool do_render = workbench_antialiasing_setup(vedata);
const bool xray_is_visible = wpd->shading.xray_alpha > 0.0f;
const bool do_transparent_infront_pass = !DRW_pass_is_empty(psl->transp_accum_infront_ps);
const bool do_transparent_pass = !DRW_pass_is_empty(psl->transp_accum_ps);
const bool do_opaque_infront_pass = !DRW_pass_is_empty(psl->opaque_infront_ps);
const bool do_opaque_pass = !DRW_pass_is_empty(psl->opaque_ps) || do_opaque_infront_pass;
if (dfbl->in_front_fb) {
GPU_framebuffer_bind(dfbl->in_front_fb);
GPU_framebuffer_clear_depth(dfbl->in_front_fb, 1.0f);
}
if (do_render) {
GPU_framebuffer_bind(dfbl->default_fb);
GPU_framebuffer_clear_color_depth_stencil(dfbl->default_fb, wpd->background_color, 1.0f, 0x00);
if (fbl->id_clear_fb) {
GPU_framebuffer_bind(fbl->id_clear_fb);
GPU_framebuffer_clear_color(fbl->id_clear_fb, clear_col);
}
if (do_opaque_pass) {
GPU_framebuffer_bind(fbl->opaque_fb);
DRW_draw_pass(psl->opaque_ps);
if (psl->shadow_ps[0]) {
DRW_draw_pass(psl->shadow_ps[0]);
DRW_draw_pass(psl->shadow_ps[1]);
}
if (do_opaque_infront_pass) {
GPU_framebuffer_bind(fbl->opaque_infront_fb);
DRW_draw_pass(psl->opaque_infront_ps);
GPU_framebuffer_bind(fbl->opaque_fb);
DRW_draw_pass(psl->merge_infront_ps);
}
GPU_framebuffer_bind(dfbl->default_fb);
DRW_draw_pass(psl->composite_ps);
if (psl->cavity_ps) {
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_draw_pass(psl->cavity_ps);
}
}
workbench_volume_draw_pass(vedata);
if (xray_is_visible) {
if (do_transparent_pass) {
GPU_framebuffer_bind(fbl->transp_accum_fb);
GPU_framebuffer_clear_color(fbl->transp_accum_fb, clear_col_with_alpha);
DRW_draw_pass(psl->transp_accum_ps);
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_draw_pass(psl->transp_resolve_ps);
}
if (do_transparent_infront_pass) {
GPU_framebuffer_bind(fbl->transp_accum_infront_fb);
GPU_framebuffer_clear_color(fbl->transp_accum_infront_fb, clear_col_with_alpha);
DRW_draw_pass(psl->transp_accum_infront_ps);
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_draw_pass(psl->transp_resolve_ps);
}
}
workbench_transparent_draw_depth_pass(vedata);
if (psl->outline_ps) {
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_draw_pass(psl->outline_ps);
}
workbench_dof_draw_pass(vedata);
}
workbench_antialiasing_draw_pass(vedata);
}
/* Viewport rendering. */
static void workbench_draw_scene(void *ved)
{
WORKBENCH_Data *vedata = ved;
WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
if (DRW_state_is_opengl_render()) {
while (wpd->taa_sample < max_ii(1, wpd->taa_sample_len)) {
workbench_update_world_ubo(wpd);
workbench_draw_sample(vedata);
}
}
else {
workbench_draw_sample(vedata);
}
workbench_draw_finish(vedata);
}
void workbench_draw_finish(void *ved)
{
WORKBENCH_Data *vedata = ved;
workbench_volume_draw_finish(vedata);
}
static void workbench_engine_free(void)
{
workbench_shader_free();
}
static void workbench_view_update(void *vedata)
{
WORKBENCH_Data *data = vedata;
workbench_antialiasing_view_updated(data);
}
static void workbench_id_update(void *UNUSED(vedata), struct ID *id)
{
if (GS(id->name) == ID_OB) {
WORKBENCH_ObjectData *oed = (WORKBENCH_ObjectData *)DRW_drawdata_get(id,
&draw_engine_workbench);
if (oed != NULL && oed->dd.recalc != 0) {
oed->shadow_bbox_dirty = (oed->dd.recalc & ID_RECALC_ALL) != 0;
oed->dd.recalc = 0;
}
}
}
static const DrawEngineDataSize workbench_data_size = DRW_VIEWPORT_DATA_SIZE(WORKBENCH_Data);
DrawEngineType draw_engine_workbench = {
NULL,
NULL,
N_("Workbench"),
&workbench_data_size,
&workbench_engine_init,
&workbench_engine_free,
&workbench_cache_init,
&workbench_cache_populate,
&workbench_cache_finish,
&workbench_draw_scene,
&workbench_view_update,
&workbench_id_update,
&workbench_render,
};
RenderEngineType DRW_engine_viewport_workbench_type = {
NULL,
NULL,
@@ -44,7 +583,7 @@ RenderEngineType DRW_engine_viewport_workbench_type = {
NULL,
NULL,
&workbench_render_update_passes,
&draw_engine_workbench_solid,
&draw_engine_workbench,
{NULL, NULL, NULL},
};

View File

@@ -23,8 +23,6 @@
#ifndef __WORKBENCH_ENGINE_H__
#define __WORKBENCH_ENGINE_H__
extern DrawEngineType draw_engine_workbench_solid;
extern DrawEngineType draw_engine_workbench_transparent;
extern RenderEngineType DRW_engine_viewport_workbench_type;
#endif /* __WORKBENCH_ENGINE_H__ */

View File

@@ -1,817 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
#include "workbench_private.h"
#include "BLI_alloca.h"
#include "BLI_dynstr.h"
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "DNA_image_types.h"
#include "DNA_fluid_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "ED_view3d.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
/* *********** STATIC *********** */
typedef struct WORKBENCH_FORWARD_Shaders {
struct GPUShader *transparent_accum_sh_cache[MAX_ACCUM_SHADERS];
struct GPUShader *object_outline_sh;
struct GPUShader *object_outline_texture_sh;
struct GPUShader *object_outline_hair_sh;
} WORKBENCH_FORWARD_Shaders;
static struct {
WORKBENCH_FORWARD_Shaders sh_data[GPU_SHADER_CFG_LEN];
struct GPUShader *composite_sh_cache[2];
struct GPUTexture *object_id_tx; /* ref only, not alloced */
struct GPUTexture *transparent_accum_tx; /* ref only, not alloced */
struct GPUTexture *transparent_revealage_tx; /* ref only, not alloced */
struct GPUTexture *composite_buffer_tx; /* ref only, not alloced */
} e_data = {{{{NULL}}}};
/* Shaders */
extern char datatoc_common_hair_lib_glsl[];
extern char datatoc_common_view_lib_glsl[];
extern char datatoc_workbench_forward_composite_frag_glsl[];
extern char datatoc_workbench_forward_depth_frag_glsl[];
extern char datatoc_workbench_forward_transparent_accum_frag_glsl[];
extern char datatoc_workbench_data_lib_glsl[];
extern char datatoc_workbench_object_outline_lib_glsl[];
extern char datatoc_workbench_curvature_lib_glsl[];
extern char datatoc_workbench_prepass_vert_glsl[];
extern char datatoc_workbench_common_lib_glsl[];
extern char datatoc_workbench_world_light_lib_glsl[];
/* static functions */
static char *workbench_build_forward_vert(bool is_hair)
{
DynStr *ds = BLI_dynstr_new();
if (is_hair) {
BLI_dynstr_append(ds, datatoc_common_hair_lib_glsl);
}
BLI_dynstr_append(ds, datatoc_common_view_lib_glsl);
BLI_dynstr_append(ds, datatoc_workbench_prepass_vert_glsl);
char *str = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
return str;
}
static char *workbench_build_forward_outline_frag(void)
{
DynStr *ds = BLI_dynstr_new();
BLI_dynstr_append(ds, datatoc_common_view_lib_glsl);
BLI_dynstr_append(ds, datatoc_workbench_forward_depth_frag_glsl);
char *str = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
return str;
}
static char *workbench_build_forward_transparent_accum_frag(void)
{
DynStr *ds = BLI_dynstr_new();
BLI_dynstr_append(ds, datatoc_common_view_lib_glsl);
BLI_dynstr_append(ds, datatoc_workbench_data_lib_glsl);
BLI_dynstr_append(ds, datatoc_workbench_common_lib_glsl);
BLI_dynstr_append(ds, datatoc_workbench_world_light_lib_glsl);
BLI_dynstr_append(ds, datatoc_workbench_forward_transparent_accum_frag_glsl);
char *str = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
return str;
}
static char *workbench_build_forward_composite_frag(void)
{
DynStr *ds = BLI_dynstr_new();
BLI_dynstr_append(ds, datatoc_workbench_data_lib_glsl);
BLI_dynstr_append(ds, datatoc_workbench_common_lib_glsl);
BLI_dynstr_append(ds, datatoc_workbench_object_outline_lib_glsl);
BLI_dynstr_append(ds, datatoc_workbench_curvature_lib_glsl);
BLI_dynstr_append(ds, datatoc_workbench_forward_composite_frag_glsl);
char *str = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
return str;
}
WORKBENCH_MaterialData *workbench_forward_get_or_create_material_data(
WORKBENCH_Data *vedata,
Object *ob,
Material *mat,
Image *ima,
ImageUser *iuser,
eV3DShadingColorType color_type,
int interp)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
WORKBENCH_FORWARD_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PassList *psl = vedata->psl;
WORKBENCH_PrivateData *wpd = stl->g_data;
WORKBENCH_MaterialData *material;
WORKBENCH_MaterialData material_template;
DRWShadingGroup *grp;
/* Solid */
workbench_material_update_data(wpd, ob, mat, &material_template, color_type);
material_template.color_type = color_type;
material_template.ima = ima;
material_template.iuser = iuser;
material_template.interp = interp;
uint hash = workbench_material_get_hash(&material_template, false);
material = BLI_ghash_lookup(wpd->material_transp_hash, POINTER_FROM_UINT(hash));
if (material == NULL) {
material = MEM_mallocN(sizeof(WORKBENCH_MaterialData), __func__);
/* transparent accum */
/* select the correct transparent accum shader */
GPUShader *shader = (wpd->shading.color_type == color_type) ?
wpd->transparent_accum_sh :
wpd->transparent_accum_uniform_sh;
const bool is_tiled = (ima && ima->source == IMA_SRC_TILED);
if (color_type == V3D_SHADING_TEXTURE_COLOR) {
shader = is_tiled ? wpd->transparent_accum_textured_array_sh :
wpd->transparent_accum_textured_sh;
}
grp = DRW_shgroup_create(shader, psl->transparent_accum_pass);
DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
DRW_shgroup_uniform_float_copy(grp, "alpha", material_template.alpha);
DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)wpd->viewvecs, 3);
workbench_material_copy(material, &material_template);
if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) {
BKE_studiolight_ensure_flag(wpd->studio_light,
STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE |
STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE);
DRW_shgroup_uniform_texture(
grp, "matcapDiffuseImage", wpd->studio_light->matcap_diffuse.gputexture);
if (workbench_is_specular_highlight_enabled(wpd)) {
DRW_shgroup_uniform_texture(
grp, "matcapSpecularImage", wpd->studio_light->matcap_specular.gputexture);
}
}
if (workbench_is_specular_highlight_enabled(wpd) || MATCAP_ENABLED(wpd)) {
DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
}
if (SHADOW_ENABLED(wpd)) {
DRW_shgroup_uniform_float_copy(grp, "shadowMultiplier", wpd->shadow_multiplier);
DRW_shgroup_uniform_float_copy(grp, "shadowShift", wpd->shadow_shift);
DRW_shgroup_uniform_float_copy(grp, "shadowFocus", wpd->shadow_focus);
}
workbench_material_shgroup_uniform(wpd, grp, material, ob, false, is_tiled, interp);
material->shgrp = grp;
/* Depth */
if (color_type == V3D_SHADING_TEXTURE_COLOR) {
material->shgrp_object_outline = DRW_shgroup_create(sh_data->object_outline_texture_sh,
psl->object_outline_pass);
GPUTexture *tex = GPU_texture_from_blender(
material->ima, material->iuser, NULL, GL_TEXTURE_2D);
DRW_shgroup_uniform_texture(material->shgrp_object_outline, "image", tex);
}
else {
material->shgrp_object_outline = DRW_shgroup_create(sh_data->object_outline_sh,
psl->object_outline_pass);
}
if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) {
DRW_shgroup_state_enable(material->shgrp_object_outline, DRW_STATE_CLIP_PLANES);
}
BLI_ghash_insert(wpd->material_transp_hash, POINTER_FROM_UINT(hash), material);
}
return material;
}
static GPUShader *ensure_forward_accum_shaders(WORKBENCH_PrivateData *wpd,
bool is_uniform_color,
bool is_hair,
bool is_tiled,
const WORKBENCH_ColorOverride color_override,
eGPUShaderConfig sh_cfg)
{
WORKBENCH_FORWARD_Shaders *sh_data = &e_data.sh_data[sh_cfg];
int index = workbench_material_get_accum_shader_index(
wpd, is_uniform_color, is_hair, is_tiled, color_override);
if (sh_data->transparent_accum_sh_cache[index] == NULL) {
const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[sh_cfg];
char *defines = workbench_material_build_defines(
wpd, is_uniform_color, is_hair, is_tiled, color_override);
char *transparent_accum_vert = workbench_build_forward_vert(is_hair);
char *transparent_accum_frag = workbench_build_forward_transparent_accum_frag();
sh_data->transparent_accum_sh_cache[index] = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg_data->lib, transparent_accum_vert, NULL},
.frag = (const char *[]){transparent_accum_frag, NULL},
.defs = (const char *[]){sh_cfg_data->def, defines, NULL},
});
MEM_freeN(transparent_accum_vert);
MEM_freeN(transparent_accum_frag);
MEM_freeN(defines);
}
return sh_data->transparent_accum_sh_cache[index];
}
static GPUShader *ensure_forward_composite_shaders(WORKBENCH_PrivateData *wpd)
{
int index = OBJECT_OUTLINE_ENABLED(wpd) ? 1 : 0;
if (e_data.composite_sh_cache[index] == NULL) {
char *defines = workbench_material_build_defines(
wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF);
char *composite_frag = workbench_build_forward_composite_frag();
e_data.composite_sh_cache[index] = DRW_shader_create_fullscreen(composite_frag, defines);
MEM_freeN(composite_frag);
MEM_freeN(defines);
}
return e_data.composite_sh_cache[index];
}
void workbench_forward_choose_shaders(WORKBENCH_PrivateData *wpd, eGPUShaderConfig sh_cfg)
{
wpd->composite_sh = ensure_forward_composite_shaders(wpd);
wpd->transparent_accum_sh = ensure_forward_accum_shaders(
wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg);
wpd->transparent_accum_hair_sh = ensure_forward_accum_shaders(
wpd, false, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg);
wpd->transparent_accum_uniform_sh = ensure_forward_accum_shaders(
wpd, true, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg);
wpd->transparent_accum_uniform_hair_sh = ensure_forward_accum_shaders(
wpd, true, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg);
wpd->transparent_accum_textured_sh = ensure_forward_accum_shaders(
wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg);
wpd->transparent_accum_textured_array_sh = ensure_forward_accum_shaders(
wpd, false, false, true, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg);
wpd->transparent_accum_vertex_sh = ensure_forward_accum_shaders(
wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_VERTEX, sh_cfg);
}
void workbench_forward_outline_shaders_ensure(WORKBENCH_PrivateData *wpd, eGPUShaderConfig sh_cfg)
{
WORKBENCH_FORWARD_Shaders *sh_data = &e_data.sh_data[sh_cfg];
if (sh_data->object_outline_sh == NULL) {
const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[sh_cfg];
char *defines = workbench_material_build_defines(
wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF);
char *defines_texture = workbench_material_build_defines(
wpd, true, false, false, WORKBENCH_COLOR_OVERRIDE_OFF);
char *defines_hair = workbench_material_build_defines(
wpd, false, true, false, WORKBENCH_COLOR_OVERRIDE_OFF);
char *forward_vert = workbench_build_forward_vert(false);
char *forward_frag = workbench_build_forward_outline_frag();
char *forward_hair_vert = workbench_build_forward_vert(true);
const char *define_id_pass = "#define OBJECT_ID_PASS_ENABLED\n";
sh_data->object_outline_sh = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg_data->lib, forward_vert, NULL},
.frag = (const char *[]){forward_frag, NULL},
.defs = (const char *[]){sh_cfg_data->def, defines, define_id_pass, NULL},
});
sh_data->object_outline_texture_sh = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg_data->lib, forward_vert, NULL},
.frag = (const char *[]){forward_frag, NULL},
.defs = (const char *[]){sh_cfg_data->def, defines_texture, define_id_pass, NULL},
});
sh_data->object_outline_hair_sh = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg_data->lib, forward_hair_vert, NULL},
.frag = (const char *[]){forward_frag, NULL},
.defs = (const char *[]){sh_cfg_data->def, defines_hair, define_id_pass, NULL},
});
MEM_freeN(forward_hair_vert);
MEM_freeN(forward_vert);
MEM_freeN(forward_frag);
MEM_freeN(defines);
MEM_freeN(defines_texture);
MEM_freeN(defines_hair);
}
}
/* public functions */
void workbench_forward_engine_init(WORKBENCH_Data *vedata)
{
WORKBENCH_FramebufferList *fbl = vedata->fbl;
WORKBENCH_PassList *psl = vedata->psl;
WORKBENCH_StorageList *stl = vedata->stl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const DRWContextState *draw_ctx = DRW_context_state_get();
DRWShadingGroup *grp;
if (!stl->g_data) {
/* Alloc transient pointers */
stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
}
if (!stl->effects) {
stl->effects = MEM_callocN(sizeof(*stl->effects), __func__);
workbench_effect_info_init(stl->effects);
}
WORKBENCH_PrivateData *wpd = stl->g_data;
workbench_private_data_init(wpd);
workbench_forward_outline_shaders_ensure(wpd, draw_ctx->sh_cfg);
workbench_volume_engine_init();
workbench_fxaa_engine_init();
workbench_taa_engine_init(vedata);
workbench_forward_outline_shaders_ensure(wpd, draw_ctx->sh_cfg);
workbench_forward_choose_shaders(wpd, draw_ctx->sh_cfg);
const float *viewport_size = DRW_viewport_size_get();
const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
const eGPUTextureFormat comp_tex_format = GPU_RGBA16F;
e_data.object_id_tx = DRW_texture_pool_query_2d(
size[0], size[1], GPU_R32UI, &draw_engine_workbench_transparent);
e_data.transparent_accum_tx = DRW_texture_pool_query_2d(
size[0], size[1], GPU_RGBA16F, &draw_engine_workbench_transparent);
e_data.transparent_revealage_tx = DRW_texture_pool_query_2d(
size[0], size[1], GPU_R16F, &draw_engine_workbench_transparent);
e_data.composite_buffer_tx = DRW_texture_pool_query_2d(
size[0], size[1], comp_tex_format, &draw_engine_workbench_transparent);
GPU_framebuffer_ensure_config(&fbl->object_outline_fb,
{
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
GPU_ATTACHMENT_TEXTURE(e_data.object_id_tx),
});
GPU_framebuffer_ensure_config(&fbl->transparent_accum_fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(e_data.transparent_accum_tx),
GPU_ATTACHMENT_TEXTURE(e_data.transparent_revealage_tx),
});
GPU_framebuffer_ensure_config(&fbl->composite_fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(e_data.composite_buffer_tx),
});
GPU_framebuffer_ensure_config(&fbl->effect_fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(e_data.transparent_accum_tx),
});
workbench_volume_cache_init(vedata);
DRWState clip_state = WORLD_CLIPPING_ENABLED(wpd) ? DRW_STATE_CLIP_PLANES : 0;
DRWState cull_state = CULL_BACKFACE_ENABLED(wpd) ? DRW_STATE_CULL_BACK : 0;
/* Transparency Accum */
{
int state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_OIT | cull_state | clip_state;
psl->transparent_accum_pass = DRW_pass_create("Transparent Accum", state);
}
/* Depth */
{
int state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | cull_state |
clip_state;
psl->object_outline_pass = DRW_pass_create("Object Outline Pass", state);
}
/* Composite */
{
int state = DRW_STATE_WRITE_COLOR;
if (DRW_state_is_scene_render()) {
/* Composite the scene over cleared background. */
state |= DRW_STATE_BLEND_ALPHA_PREMUL;
}
psl->composite_pass = DRW_pass_create("Composite", state);
grp = DRW_shgroup_create(wpd->composite_sh, psl->composite_pass);
if (OBJECT_ID_PASS_ENABLED(wpd)) {
DRW_shgroup_uniform_texture_ref(grp, "objectId", &e_data.object_id_tx);
}
DRW_shgroup_uniform_texture_ref(grp, "transparentAccum", &e_data.transparent_accum_tx);
DRW_shgroup_uniform_texture_ref(grp, "transparentRevealage", &e_data.transparent_revealage_tx);
DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
{
workbench_aa_create_pass(vedata, &e_data.transparent_accum_tx);
}
if (wpd->shading.type == OB_WIRE) {
wpd->shading.xray_alpha = 0.0f;
wpd->shading.xray_alpha_wire = 0.0f;
}
}
void workbench_forward_engine_free()
{
for (int sh_data_index = 0; sh_data_index < ARRAY_SIZE(e_data.sh_data); sh_data_index++) {
WORKBENCH_FORWARD_Shaders *sh_data = &e_data.sh_data[sh_data_index];
for (int index = 0; index < MAX_ACCUM_SHADERS; index++) {
DRW_SHADER_FREE_SAFE(sh_data->transparent_accum_sh_cache[index]);
}
DRW_SHADER_FREE_SAFE(sh_data->object_outline_sh);
DRW_SHADER_FREE_SAFE(sh_data->object_outline_texture_sh);
DRW_SHADER_FREE_SAFE(sh_data->object_outline_hair_sh);
}
for (int index = 0; index < 2; index++) {
DRW_SHADER_FREE_SAFE(e_data.composite_sh_cache[index]);
}
workbench_volume_engine_free();
workbench_fxaa_engine_free();
workbench_taa_engine_free();
workbench_dof_engine_free();
}
void workbench_forward_cache_init(WORKBENCH_Data *UNUSED(vedata))
{
}
static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, Object *ob)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PassList *psl = vedata->psl;
WORKBENCH_PrivateData *wpd = stl->g_data;
for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
if (md->type != eModifierType_ParticleSystem) {
continue;
}
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
continue;
}
ParticleSettings *part = psys->part;
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
if (draw_as == PART_DRAW_PATH) {
const DRWContextState *draw_ctx = DRW_context_state_get();
Material *mat;
Image *image;
ImageUser *iuser;
int interp;
workbench_material_get_image_and_mat(ob, part->omat, &image, &iuser, &interp, &mat);
eV3DShadingColorType color_type = workbench_material_determine_color_type(
wpd, image, ob, false);
WORKBENCH_MaterialData *material = workbench_forward_get_or_create_material_data(
vedata, ob, mat, image, iuser, color_type, interp);
struct GPUShader *shader = (wpd->shading.color_type == color_type) ?
wpd->transparent_accum_hair_sh :
wpd->transparent_accum_uniform_hair_sh;
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(
ob, psys, md, psl->transparent_accum_pass, shader);
DRW_shgroup_uniform_block(shgrp, "world_block", wpd->world_ubo);
workbench_material_shgroup_uniform(wpd, shgrp, material, ob, false, false, interp);
DRW_shgroup_uniform_vec4(shgrp, "viewvecs[0]", (float *)wpd->viewvecs, 3);
/* Hairs have lots of layer and can rapidly become the most prominent surface.
* So lower their alpha artificially. */
float hair_alpha = XRAY_ALPHA(wpd) * 0.33f;
DRW_shgroup_uniform_float_copy(shgrp, "alpha", hair_alpha);
if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) {
BKE_studiolight_ensure_flag(wpd->studio_light,
STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE |
STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE);
DRW_shgroup_uniform_texture(
shgrp, "matcapDiffuseImage", wpd->studio_light->matcap_diffuse.gputexture);
if (workbench_is_specular_highlight_enabled(wpd)) {
DRW_shgroup_uniform_texture(
shgrp, "matcapSpecularImage", wpd->studio_light->matcap_specular.gputexture);
}
}
if (workbench_is_specular_highlight_enabled(wpd) || MATCAP_ENABLED(wpd)) {
DRW_shgroup_uniform_vec2(shgrp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
}
WORKBENCH_FORWARD_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
shgrp = DRW_shgroup_hair_create(
ob, psys, md, vedata->psl->object_outline_pass, sh_data->object_outline_hair_sh);
}
}
}
static void workbench_forward_cache_populate_texture_paint_mode(WORKBENCH_Data *vedata, Object *ob)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PrivateData *wpd = stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) &&
!DRW_state_is_image_render();
WORKBENCH_MaterialData *material;
/* Force workbench to render active object textured when in texture paint mode */
const ImagePaintSettings *imapaint = &scene->toolsettings->imapaint;
/* Single Image mode */
if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) {
Image *image = imapaint->canvas;
int interp = (imapaint->interp == IMAGEPAINT_INTERP_LINEAR) ? SHD_INTERP_LINEAR :
SHD_INTERP_CLOSEST;
eV3DShadingColorType color_type = workbench_material_determine_color_type(
wpd, image, ob, use_sculpt_pbvh);
struct GPUBatch *geom = DRW_cache_mesh_surface_texpaint_single_get(ob);
material = workbench_forward_get_or_create_material_data(
vedata, ob, NULL, image, NULL, color_type, interp);
DRW_shgroup_call(material->shgrp, geom, ob);
DRW_shgroup_call(material->shgrp_object_outline, geom, ob);
}
else {
/* IMAGEPAINT_MODE_MATERIAL */
const int materials_len = DRW_cache_object_material_count_get(ob);
struct GPUBatch **geom_array = DRW_cache_mesh_surface_texpaint_get(ob);
for (int i = 0; i < materials_len; i++) {
if (geom_array != NULL && geom_array[i] != NULL) {
Material *mat;
Image *image;
ImageUser *iuser;
int interp;
workbench_material_get_image_and_mat(ob, i + 1, &image, &iuser, &interp, &mat);
eV3DShadingColorType color_type = workbench_material_determine_color_type(
wpd, image, ob, use_sculpt_pbvh);
material = workbench_forward_get_or_create_material_data(
vedata, ob, mat, image, iuser, color_type, interp);
DRW_shgroup_call(material->shgrp, geom_array[i], ob);
DRW_shgroup_call(material->shgrp_object_outline, geom_array[i], ob);
}
}
}
}
static void workbench_forward_cache_populate_vertex_paint_mode(WORKBENCH_Data *vedata, Object *ob)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PrivateData *wpd = stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) &&
!DRW_state_is_image_render();
WORKBENCH_MaterialData *material;
eV3DShadingColorType color_type = workbench_material_determine_color_type(
wpd, NULL, ob, use_sculpt_pbvh);
struct GPUBatch *geom = DRW_cache_mesh_surface_vertpaint_get(ob);
material = workbench_forward_get_or_create_material_data(
vedata, ob, NULL, NULL, NULL, color_type, false);
DRW_shgroup_call(material->shgrp, geom, ob);
DRW_shgroup_call(material->shgrp_object_outline, geom, ob);
}
void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PrivateData *wpd = stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
const bool is_wire = (ob->dt == OB_WIRE);
if (!DRW_object_is_renderable(ob)) {
return;
}
if (ob->type == OB_MESH) {
workbench_forward_cache_populate_particles(vedata, ob);
}
ModifierData *md;
if (((ob->base_flag & BASE_FROM_DUPLI) == 0) &&
(md = modifiers_findByType(ob, eModifierType_Fluid)) &&
(modifier_isEnabled(scene, md, eModifierMode_Realtime)) &&
(((FluidModifierData *)md)->domain != NULL) &&
(((FluidModifierData *)md)->domain->type == FLUID_DOMAIN_TYPE_GAS)) {
workbench_volume_cache_populate(vedata, scene, ob, md);
return; /* Do not draw solid in this case. */
}
if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) {
return;
}
if (ob->dt < OB_WIRE) {
return;
}
WORKBENCH_MaterialData *material = NULL;
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) {
const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) &&
!DRW_state_is_image_render();
const int materials_len = DRW_cache_object_material_count_get(ob);
const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL;
const WORKBENCH_ColorOverride color_override = workbench_object_color_override_get(ob);
const bool use_texture_paint_drawing = !(DRW_state_is_image_render() &&
draw_ctx->v3d == NULL) &&
(color_override == WORKBENCH_COLOR_OVERRIDE_TEXTURE) &&
me && me->mloopuv;
const bool use_vertex_paint_drawing = !(DRW_state_is_image_render() &&
draw_ctx->v3d == NULL) &&
(color_override == WORKBENCH_COLOR_OVERRIDE_VERTEX) &&
me && me->mloopcol;
if (use_texture_paint_drawing) {
workbench_forward_cache_populate_texture_paint_mode(vedata, ob);
}
else if (use_vertex_paint_drawing) {
workbench_forward_cache_populate_vertex_paint_mode(vedata, ob);
}
else if (!use_sculpt_pbvh && TEXTURE_DRAWING_ENABLED(wpd) && me && me->mloopuv) {
struct GPUBatch **geom_array = DRW_cache_mesh_surface_texpaint_get(ob);
for (int i = 0; i < materials_len; i++) {
Material *mat;
Image *image;
ImageUser *iuser;
int interp;
workbench_material_get_image_and_mat(ob, i + 1, &image, &iuser, &interp, &mat);
eV3DShadingColorType color_type = workbench_material_determine_color_type(
wpd, image, ob, use_sculpt_pbvh);
material = workbench_forward_get_or_create_material_data(
vedata, ob, mat, image, iuser, color_type, interp);
DRW_shgroup_call(material->shgrp_object_outline, geom_array[i], ob);
DRW_shgroup_call(material->shgrp, geom_array[i], ob);
}
}
else if (ELEM(wpd->shading.color_type,
V3D_SHADING_SINGLE_COLOR,
V3D_SHADING_OBJECT_COLOR,
V3D_SHADING_RANDOM_COLOR,
V3D_SHADING_VERTEX_COLOR)) {
/* No material split needed */
eV3DShadingColorType color_type = workbench_material_determine_color_type(
wpd, NULL, ob, use_sculpt_pbvh);
if (use_sculpt_pbvh) {
material = workbench_forward_get_or_create_material_data(
vedata, ob, NULL, NULL, NULL, color_type, 0);
bool use_vcol = (color_type == V3D_SHADING_VERTEX_COLOR);
/* TODO(fclem) make this call optional */
DRW_shgroup_call_sculpt(material->shgrp_object_outline, ob, false, false, false);
if (!is_wire) {
DRW_shgroup_call_sculpt(material->shgrp, ob, false, false, use_vcol);
}
}
else {
struct GPUBatch *geom = (color_type == V3D_SHADING_VERTEX_COLOR) ?
DRW_cache_mesh_surface_vertpaint_get(ob) :
DRW_cache_object_surface_get(ob);
if (geom) {
material = workbench_forward_get_or_create_material_data(
vedata, ob, NULL, NULL, NULL, color_type, 0);
/* TODO(fclem) make this call optional */
DRW_shgroup_call(material->shgrp_object_outline, geom, ob);
if (!is_wire) {
DRW_shgroup_call(material->shgrp, geom, ob);
}
}
}
}
else {
/* Draw material color */
if (use_sculpt_pbvh) {
struct DRWShadingGroup **shgrps = BLI_array_alloca(shgrps, materials_len);
for (int i = 0; i < materials_len; i++) {
struct Material *mat = BKE_object_material_get(ob, i + 1);
material = workbench_forward_get_or_create_material_data(
vedata, ob, mat, NULL, NULL, V3D_SHADING_MATERIAL_COLOR, 0);
shgrps[i] = material->shgrp;
}
/* TODO(fclem) make this call optional */
DRW_shgroup_call_sculpt(material->shgrp_object_outline, ob, false, false, false);
if (!is_wire) {
DRW_shgroup_call_sculpt_with_materials(shgrps, ob, false);
}
}
else {
struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len);
struct GPUBatch **mat_geom = DRW_cache_object_surface_material_get(
ob, gpumat_array, materials_len);
if (mat_geom) {
for (int i = 0; i < materials_len; i++) {
if (mat_geom[i] == NULL) {
continue;
}
Material *mat = BKE_object_material_get(ob, i + 1);
material = workbench_forward_get_or_create_material_data(
vedata, ob, mat, NULL, NULL, V3D_SHADING_MATERIAL_COLOR, 0);
/* TODO(fclem) make this call optional */
DRW_shgroup_call(material->shgrp_object_outline, mat_geom[i], ob);
if (!is_wire) {
DRW_shgroup_call(material->shgrp, mat_geom[i], ob);
}
}
}
}
}
}
}
void workbench_forward_cache_finish(WORKBENCH_Data *UNUSED(vedata))
{
}
void workbench_forward_draw_scene(WORKBENCH_Data *vedata)
{
WORKBENCH_PassList *psl = vedata->psl;
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_FramebufferList *fbl = vedata->fbl;
WORKBENCH_PrivateData *wpd = stl->g_data;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
if (dfbl->in_front_fb) {
/* TODO(fclem) This clear should be done in a global place. */
GPU_framebuffer_bind(dfbl->in_front_fb);
GPU_framebuffer_clear_depth(dfbl->in_front_fb, 1.0f);
}
if (workbench_is_taa_enabled(wpd)) {
workbench_taa_draw_scene_start(vedata);
}
/* Write Depth + Object ID */
const float clear_outline[4] = {0.0f};
GPU_framebuffer_bind(fbl->object_outline_fb);
GPU_framebuffer_clear_color(fbl->object_outline_fb, clear_outline);
DRW_draw_pass(psl->object_outline_pass);
if (XRAY_ALPHA(wpd) > 0.0) {
const float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
GPU_framebuffer_bind(fbl->transparent_accum_fb);
GPU_framebuffer_clear_color(fbl->transparent_accum_fb, clear_color);
DRW_draw_pass(psl->transparent_accum_pass);
}
else {
/* TODO(fclem): this is unnecessary and takes up perf.
* Better change the composite frag shader to not use the tx. */
const float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
GPU_framebuffer_bind(fbl->transparent_accum_fb);
GPU_framebuffer_clear_color(fbl->transparent_accum_fb, clear_color);
}
/* Composite */
GPU_framebuffer_bind(fbl->composite_fb);
if (DRW_state_is_scene_render()) {
float clear_color[4];
workbench_clear_color_get(clear_color);
GPU_framebuffer_clear_color(fbl->composite_fb, clear_color);
}
DRW_draw_pass(psl->composite_pass);
DRW_draw_pass(psl->volume_pass);
/* Only when clipping is enabled. */
if (psl->background_pass) {
DRW_draw_pass(psl->background_pass);
}
/* Color correct and Anti aliasing */
workbench_aa_draw_pass(vedata, e_data.composite_buffer_tx);
}
void workbench_forward_draw_finish(WORKBENCH_Data *vedata)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PrivateData *wpd = stl->g_data;
workbench_private_data_free(wpd);
workbench_volume_smoke_textures_free(wpd);
}

View File

@@ -22,6 +22,8 @@
#include "workbench_private.h"
#include "BLI_memblock.h"
#include "BKE_image.h"
#include "BKE_node.h"
@@ -31,310 +33,78 @@
#include "DNA_node_types.h"
#include "DNA_mesh_types.h"
#include "GPU_uniformbuffer.h"
#include "ED_uvedit.h"
#define HSV_SATURATION 0.5
#define HSV_VALUE 0.8
void workbench_material_update_data(WORKBENCH_PrivateData *wpd,
Object *ob,
Material *mat,
WORKBENCH_MaterialData *data,
int color_type)
void workbench_material_ubo_data(WORKBENCH_PrivateData *wpd,
Object *ob,
Material *mat,
WORKBENCH_UBO_Material *data,
eV3DShadingColorType color_type)
{
data->metallic = 0.0f;
data->roughness = 0.632455532f; /* sqrtf(0.4f); */
data->alpha = wpd->shading.xray_alpha;
float metallic = 0.0f;
float roughness = 0.632455532f; /* sqrtf(0.4f); */
float alpha = wpd->shading.xray_alpha;
if (color_type == V3D_SHADING_SINGLE_COLOR) {
copy_v3_v3(data->base_color, wpd->shading.single_color);
}
else if (color_type == V3D_SHADING_ERROR_COLOR) {
copy_v3_fl3(data->base_color, 0.8, 0.0, 0.8);
}
else if (color_type == V3D_SHADING_RANDOM_COLOR) {
uint hash = BLI_ghashutil_strhash_p_murmur(ob->id.name);
if (ob->id.lib) {
hash = (hash * 13) ^ BLI_ghashutil_strhash_p_murmur(ob->id.lib->name);
}
float hue = BLI_hash_int_01(hash);
float hsv[3] = {hue, HSV_SATURATION, HSV_VALUE};
hsv_to_rgb_v(hsv, data->base_color);
}
else if (ELEM(color_type, V3D_SHADING_OBJECT_COLOR, V3D_SHADING_VERTEX_COLOR)) {
data->alpha *= ob->color[3];
copy_v3_v3(data->base_color, ob->color);
}
else {
/* V3D_SHADING_MATERIAL_COLOR or V3D_SHADING_TEXTURE_COLOR */
if (mat) {
data->alpha *= mat->a;
copy_v3_v3(data->base_color, &mat->r);
if (workbench_is_specular_highlight_enabled(wpd)) {
data->metallic = mat->metallic;
data->roughness = sqrtf(mat->roughness); /* Remap to disney roughness. */
switch (color_type) {
case V3D_SHADING_SINGLE_COLOR:
copy_v3_v3(data->base_color, wpd->shading.single_color);
break;
case V3D_SHADING_RANDOM_COLOR: {
uint hash = BLI_ghashutil_strhash_p_murmur(ob->id.name);
if (ob->id.lib) {
hash = (hash * 13) ^ BLI_ghashutil_strhash_p_murmur(ob->id.lib->name);
}
float hue = BLI_hash_int_01(hash);
float hsv[3] = {hue, HSV_SATURATION, HSV_VALUE};
hsv_to_rgb_v(hsv, data->base_color);
break;
}
else {
copy_v3_fl(data->base_color, 0.8f);
}
case V3D_SHADING_OBJECT_COLOR:
case V3D_SHADING_VERTEX_COLOR:
alpha *= ob->color[3];
copy_v3_v3(data->base_color, ob->color);
break;
case V3D_SHADING_MATERIAL_COLOR:
case V3D_SHADING_TEXTURE_COLOR:
default:
if (mat) {
alpha *= mat->a;
copy_v3_v3(data->base_color, &mat->r);
metallic = mat->metallic;
roughness = sqrtf(mat->roughness); /* Remap to disney roughness. */
}
else {
copy_v3_fl(data->base_color, 0.8f);
}
break;
}
uint32_t packed_metallic = unit_float_to_uchar_clamp(metallic);
uint32_t packed_roughness = unit_float_to_uchar_clamp(roughness);
uint32_t packed_alpha = unit_float_to_uchar_clamp(alpha);
data->packed_data = (packed_alpha << 16u) | (packed_roughness << 8u) | packed_metallic;
}
char *workbench_material_build_defines(WORKBENCH_PrivateData *wpd,
bool is_uniform_color,
bool is_hair,
bool is_tiled,
const WORKBENCH_ColorOverride color_override)
/* Return correct material or empty default material if slot is empty. */
BLI_INLINE Material *workbench_object_material_get(Object *ob, int mat_nr)
{
char *str = NULL;
bool use_textures = (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR) && !is_uniform_color;
bool use_vertex_colors = (wpd->shading.color_type == V3D_SHADING_VERTEX_COLOR) &&
!is_uniform_color;
switch (color_override) {
case WORKBENCH_COLOR_OVERRIDE_TEXTURE:
use_textures = true;
use_vertex_colors = false;
is_hair = false;
break;
case WORKBENCH_COLOR_OVERRIDE_VERTEX:
use_textures = false;
use_vertex_colors = true;
is_hair = false;
is_tiled = false;
break;
case WORKBENCH_COLOR_OVERRIDE_OFF:
break;
Material *ma = BKE_object_material_get(ob, mat_nr);
if (ma == NULL) {
ma = BKE_material_default_empty();
}
DynStr *ds = BLI_dynstr_new();
if (wpd->shading.flag & V3D_SHADING_OBJECT_OUTLINE) {
BLI_dynstr_append(ds, "#define V3D_SHADING_OBJECT_OUTLINE\n");
}
if (wpd->shading.flag & V3D_SHADING_SHADOW) {
BLI_dynstr_append(ds, "#define V3D_SHADING_SHADOW\n");
}
if (SSAO_ENABLED(wpd) || CURVATURE_ENABLED(wpd)) {
BLI_dynstr_append(ds, "#define WB_CAVITY\n");
}
if (workbench_is_specular_highlight_enabled(wpd)) {
BLI_dynstr_append(ds, "#define V3D_SHADING_SPECULAR_HIGHLIGHT\n");
}
if (STUDIOLIGHT_ENABLED(wpd)) {
BLI_dynstr_append(ds, "#define V3D_LIGHTING_STUDIO\n");
}
if (FLAT_ENABLED(wpd)) {
BLI_dynstr_append(ds, "#define V3D_LIGHTING_FLAT\n");
}
if (MATCAP_ENABLED(wpd)) {
BLI_dynstr_append(ds, "#define V3D_LIGHTING_MATCAP\n");
}
if (OBJECT_ID_PASS_ENABLED(wpd)) {
BLI_dynstr_append(ds, "#define OBJECT_ID_PASS_ENABLED\n");
}
if (workbench_is_matdata_pass_enabled(wpd)) {
BLI_dynstr_append(ds, "#define MATDATA_PASS_ENABLED\n");
}
if (NORMAL_VIEWPORT_PASS_ENABLED(wpd)) {
BLI_dynstr_append(ds, "#define NORMAL_VIEWPORT_PASS_ENABLED\n");
}
if (use_vertex_colors) {
BLI_dynstr_append(ds, "#define V3D_SHADING_VERTEX_COLOR\n");
}
if (use_textures) {
BLI_dynstr_append(ds, "#define V3D_SHADING_TEXTURE_COLOR\n");
}
if (NORMAL_ENCODING_ENABLED()) {
BLI_dynstr_append(ds, "#define WORKBENCH_ENCODE_NORMALS\n");
}
if (is_hair) {
BLI_dynstr_append(ds, "#define HAIR_SHADER\n");
}
if (use_textures && is_tiled) {
BLI_dynstr_append(ds, "#define TEXTURE_IMAGE_ARRAY\n");
}
str = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
return str;
return ma;
}
uint workbench_material_get_hash(WORKBENCH_MaterialData *mat, bool is_ghost)
{
union {
struct {
/* WHATCH: Keep in sync with View3DShading.color_type max value. */
uchar color_type;
uchar diff_r;
uchar diff_g;
uchar diff_b;
uchar alpha;
uchar ghost;
uchar metal;
uchar roughness;
void *ima;
};
/* HACK to ensure input is 4 uint long. */
uint a[4];
} input = {.color_type = (uchar)(mat->color_type),
.diff_r = (uchar)(mat->base_color[0] * 0xFF),
.diff_g = (uchar)(mat->base_color[1] * 0xFF),
.diff_b = (uchar)(mat->base_color[2] * 0xFF),
.alpha = (uint)(mat->alpha * 0xFF),
.ghost = (uchar)is_ghost,
.metal = (uchar)(mat->metallic * 0xFF),
.roughness = (uchar)(mat->roughness * 0xFF),
.ima = mat->ima};
BLI_assert(sizeof(input) == sizeof(uint) * 4);
return BLI_ghashutil_uinthash_v4((uint *)&input);
}
int workbench_material_get_composite_shader_index(WORKBENCH_PrivateData *wpd)
{
/* NOTE: change MAX_COMPOSITE_SHADERS accordingly when modifying this function. */
int index = 0;
/* 2 bits FLAT/STUDIO/MATCAP + Specular highlight */
index = wpd->shading.light;
SET_FLAG_FROM_TEST(index, wpd->shading.flag & V3D_SHADING_SHADOW, 1 << 2);
SET_FLAG_FROM_TEST(index, wpd->shading.flag & V3D_SHADING_CAVITY, 1 << 3);
SET_FLAG_FROM_TEST(index, wpd->shading.flag & V3D_SHADING_OBJECT_OUTLINE, 1 << 4);
SET_FLAG_FROM_TEST(index, workbench_is_matdata_pass_enabled(wpd), 1 << 5);
SET_FLAG_FROM_TEST(index, workbench_is_specular_highlight_enabled(wpd), 1 << 6);
BLI_assert(index < MAX_COMPOSITE_SHADERS);
return index;
}
int workbench_material_get_prepass_shader_index(WORKBENCH_PrivateData *wpd,
bool is_uniform_color,
bool is_hair,
bool is_tiled,
const WORKBENCH_ColorOverride color_override)
{
bool use_textures = (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR) && !is_uniform_color;
bool use_vertex_colors = (wpd->shading.color_type == V3D_SHADING_VERTEX_COLOR) &&
!is_uniform_color;
switch (color_override) {
case WORKBENCH_COLOR_OVERRIDE_TEXTURE:
use_textures = true;
use_vertex_colors = false;
break;
case WORKBENCH_COLOR_OVERRIDE_VERTEX:
use_textures = false;
use_vertex_colors = true;
is_tiled = false;
break;
case WORKBENCH_COLOR_OVERRIDE_OFF:
break;
}
/* NOTE: change MAX_PREPASS_SHADERS accordingly when modifying this function. */
int index = 0;
SET_FLAG_FROM_TEST(index, is_hair, 1 << 0);
SET_FLAG_FROM_TEST(index, workbench_is_matdata_pass_enabled(wpd), 1 << 1);
SET_FLAG_FROM_TEST(index, OBJECT_ID_PASS_ENABLED(wpd), 1 << 2);
SET_FLAG_FROM_TEST(index, NORMAL_VIEWPORT_PASS_ENABLED(wpd), 1 << 3);
SET_FLAG_FROM_TEST(index, MATCAP_ENABLED(wpd), 1 << 4);
SET_FLAG_FROM_TEST(index, use_textures, 1 << 5);
SET_FLAG_FROM_TEST(index, use_vertex_colors, 1 << 6);
SET_FLAG_FROM_TEST(index, is_tiled && use_textures, 1 << 7);
BLI_assert(index < MAX_PREPASS_SHADERS);
return index;
}
int workbench_material_get_accum_shader_index(WORKBENCH_PrivateData *wpd,
bool is_uniform_color,
bool is_hair,
bool is_tiled,
const WORKBENCH_ColorOverride color_override)
{
bool use_textures = (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR) && !is_uniform_color;
bool use_vertex_colors = (wpd->shading.color_type == V3D_SHADING_VERTEX_COLOR) &&
!is_uniform_color;
switch (color_override) {
case WORKBENCH_COLOR_OVERRIDE_TEXTURE:
use_textures = true;
use_vertex_colors = false;
is_hair = false;
break;
case WORKBENCH_COLOR_OVERRIDE_VERTEX:
use_textures = false;
use_vertex_colors = true;
is_hair = false;
is_tiled = false;
break;
case WORKBENCH_COLOR_OVERRIDE_OFF:
break;
}
/* NOTE: change MAX_ACCUM_SHADERS accordingly when modifying this function. */
int index = 0;
/* 2 bits FLAT/STUDIO/MATCAP + Specular highlight */
index = wpd->shading.light;
SET_FLAG_FROM_TEST(index, use_textures, 1 << 2);
SET_FLAG_FROM_TEST(index, use_vertex_colors, 1 << 3);
SET_FLAG_FROM_TEST(index, is_hair, 1 << 4);
/* 1 bits SHADOWS (only facing factor) */
SET_FLAG_FROM_TEST(index, SHADOW_ENABLED(wpd), 1 << 5);
SET_FLAG_FROM_TEST(index, workbench_is_specular_highlight_enabled(wpd), 1 << 6);
SET_FLAG_FROM_TEST(index, is_tiled && use_textures, 1 << 7);
BLI_assert(index < MAX_ACCUM_SHADERS);
return index;
}
eV3DShadingColorType workbench_material_determine_color_type(WORKBENCH_PrivateData *wpd,
Image *ima,
Object *ob,
bool use_sculpt_pbvh)
{
eV3DShadingColorType color_type = wpd->shading.color_type;
const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL;
if ((color_type == V3D_SHADING_TEXTURE_COLOR) &&
(ima == NULL || use_sculpt_pbvh || (ob->dt < OB_TEXTURE))) {
color_type = V3D_SHADING_MATERIAL_COLOR;
}
if (color_type == V3D_SHADING_VERTEX_COLOR && (me == NULL || me->mloopcol == NULL)) {
color_type = V3D_SHADING_OBJECT_COLOR;
}
switch (workbench_object_color_override_get(ob)) {
/* Force V3D_SHADING_TEXTURE_COLOR for active object when in texture painting
* no matter the shading color that the user has chosen, when there is no
* texture we will render the object with the error color */
case WORKBENCH_COLOR_OVERRIDE_TEXTURE:
color_type = ima ? V3D_SHADING_TEXTURE_COLOR : V3D_SHADING_ERROR_COLOR;
break;
/* Force V3D_SHADING_VERTEX_COLOR for active object when in vertex painting
* no matter the shading color that the user has chosen, when there is no
* vertex color we will render the object with the error color */
case WORKBENCH_COLOR_OVERRIDE_VERTEX:
color_type = V3D_SHADING_VERTEX_COLOR;
break;
case WORKBENCH_COLOR_OVERRIDE_OFF:
break;
}
return color_type;
}
void workbench_material_get_image_and_mat(
Object *ob, int mat_nr, Image **r_image, ImageUser **r_iuser, int *r_interp, Material **r_mat)
BLI_INLINE void workbench_material_get_image(
Object *ob, int mat_nr, Image **r_image, ImageUser **r_iuser, int *r_interp)
{
bNode *node;
*r_mat = BKE_object_material_get(ob, mat_nr);
ED_object_get_active_image(ob, mat_nr, r_image, r_iuser, &node, NULL);
if (node && *r_image) {
switch (node->type) {
@@ -358,53 +128,170 @@ void workbench_material_get_image_and_mat(
}
}
void workbench_material_shgroup_uniform(WORKBENCH_PrivateData *wpd,
DRWShadingGroup *grp,
WORKBENCH_MaterialData *material,
Object *ob,
const bool deferred,
const bool is_tiled,
const int interp)
/* Return true if the current material ubo has changed and needs to be rebind. */
BLI_INLINE bool workbench_material_chunk_select(WORKBENCH_PrivateData *wpd,
uint32_t id,
uint32_t *r_mat_id)
{
if (deferred && !workbench_is_matdata_pass_enabled(wpd)) {
return;
bool resource_changed = false;
/* Divide in chunks of MAX_MATERIAL. */
uint32_t chunk = id >> 12u;
*r_mat_id = id & 0xFFFu;
/* We need to add a new chunk. */
while (chunk >= wpd->material_chunk_count) {
wpd->material_chunk_count++;
wpd->material_ubo_data_curr = BLI_memblock_alloc(wpd->material_ubo_data);
wpd->material_ubo_curr = workbench_material_ubo_alloc(wpd);
wpd->material_chunk_curr = chunk;
resource_changed = true;
}
/* We need to go back to a previous chunk. */
if (wpd->material_chunk_curr != chunk) {
wpd->material_ubo_data_curr = BLI_memblock_elem_get(wpd->material_ubo_data, 0, chunk);
wpd->material_ubo_curr = BLI_memblock_elem_get(wpd->material_ubo, 0, chunk);
wpd->material_chunk_curr = chunk;
resource_changed = true;
}
return resource_changed;
}
DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd,
Object *ob,
int mat_nr,
eV3DShadingColorType color_type,
bool hair,
bool *r_transp)
{
Image *ima = NULL;
ImageUser *iuser = NULL;
int interp;
const bool infront = (ob->dtx & OB_DRAWXRAY) != 0;
if (color_type == V3D_SHADING_TEXTURE_COLOR) {
workbench_material_get_image(ob, mat_nr, &ima, &iuser, &interp);
if (ima == NULL) {
/* Fallback to material color. */
color_type = V3D_SHADING_MATERIAL_COLOR;
}
}
const bool use_highlight = workbench_is_specular_highlight_enabled(wpd);
const bool use_texture = (V3D_SHADING_TEXTURE_COLOR == workbench_material_determine_color_type(
wpd, material->ima, ob, false));
if (use_texture) {
if (is_tiled) {
GPUTexture *array_tex = GPU_texture_from_blender(
material->ima, material->iuser, NULL, GL_TEXTURE_2D_ARRAY);
GPUTexture *data_tex = GPU_texture_from_blender(
material->ima, material->iuser, NULL, GL_TEXTURE_1D_ARRAY);
DRW_shgroup_uniform_texture(grp, "image_tile_array", array_tex);
DRW_shgroup_uniform_texture(grp, "image_tile_data", data_tex);
switch (color_type) {
case V3D_SHADING_TEXTURE_COLOR: {
return workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, interp, hair);
}
case V3D_SHADING_MATERIAL_COLOR: {
/* For now, we use the same ubo for material and object coloring but with different indices.
* This means they are mutually exclusive. */
BLI_assert(
ELEM(wpd->shading.color_type, V3D_SHADING_MATERIAL_COLOR, V3D_SHADING_TEXTURE_COLOR));
Material *ma = workbench_object_material_get(ob, mat_nr);
const bool transp = wpd->shading.xray_alpha < 1.0f || ma->a < 1.0f;
WORKBENCH_Prepass *prepass = &wpd->prepass[transp][infront][hair];
if (r_transp && transp) {
*r_transp = true;
}
DRWShadingGroup **grp_mat = NULL;
/* A hashmap stores material shgroups to pack all similar drawcalls together. */
if (BLI_ghash_ensure_p(prepass->material_hash, ma, (void ***)&grp_mat)) {
return *grp_mat;
}
uint32_t mat_id, id = wpd->material_index++;
workbench_material_chunk_select(wpd, id, &mat_id);
workbench_material_ubo_data(wpd, ob, ma, &wpd->material_ubo_data_curr[mat_id], color_type);
DRWShadingGroup *grp = prepass->common_shgrp;
*grp_mat = grp = DRW_shgroup_create_sub(grp);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", mat_id);
return grp;
}
case V3D_SHADING_VERTEX_COLOR: {
const bool transp = wpd->shading.xray_alpha < 1.0f;
DRWShadingGroup *grp = wpd->prepass[transp][infront][hair].vcol_shgrp;
return grp;
}
default: {
/* For now, we use the same ubo for material and object coloring but with different indices.
* This means they are mutually exclusive. */
BLI_assert(
!ELEM(wpd->shading.color_type, V3D_SHADING_MATERIAL_COLOR, V3D_SHADING_TEXTURE_COLOR));
uint32_t mat_id, id = DRW_object_resource_id_get(ob);
bool resource_changed = workbench_material_chunk_select(wpd, id, &mat_id);
workbench_material_ubo_data(wpd, ob, NULL, &wpd->material_ubo_data_curr[mat_id], color_type);
const bool transp = wpd->shading.xray_alpha < 1.0f || ob->color[3] < 1.0f;
DRWShadingGroup *grp = wpd->prepass[transp][infront][hair].common_shgrp;
if (resource_changed) {
grp = DRW_shgroup_create_sub(grp);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
}
if (r_transp && transp) {
*r_transp = true;
}
return grp;
}
}
}
/* If ima is null, search appropriate image node but will fallback to purple texture otherwise. */
DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd,
Object *ob,
int mat_nr,
Image *ima,
ImageUser *iuser,
int interp,
bool hair)
{
GPUTexture *tex = NULL, *tex_tile_data = NULL;
if (ima == NULL) {
workbench_material_get_image(ob, mat_nr, &ima, &iuser, &interp);
}
if (ima) {
if (ima->source == IMA_SRC_TILED) {
tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D_ARRAY);
tex_tile_data = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_1D_ARRAY);
}
else {
GPUTexture *tex = GPU_texture_from_blender(
material->ima, material->iuser, NULL, GL_TEXTURE_2D);
DRW_shgroup_uniform_texture(grp, "image", tex);
tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D);
}
DRW_shgroup_uniform_bool_copy(
grp, "imagePremultiplied", (material->ima->alpha_mode == IMA_ALPHA_PREMUL));
DRW_shgroup_uniform_bool_copy(grp, "imageNearest", (interp == SHD_INTERP_CLOSEST));
}
DRW_shgroup_uniform_vec4(grp, "materialColorAndMetal", material->base_color, 1);
if (use_highlight) {
DRW_shgroup_uniform_float(grp, "materialRoughness", &material->roughness, 1);
if (tex == NULL) {
printf("Image not foudn\n");
tex = wpd->dummy_image_tx;
}
}
void workbench_material_copy(WORKBENCH_MaterialData *dest_material,
const WORKBENCH_MaterialData *source_material)
{
copy_v3_v3(dest_material->base_color, source_material->base_color);
dest_material->metallic = source_material->metallic;
dest_material->roughness = source_material->roughness;
dest_material->ima = source_material->ima;
dest_material->iuser = source_material->iuser;
const bool infront = (ob->dtx & OB_DRAWXRAY) != 0;
const bool transp = wpd->shading.xray_alpha < 1.0f;
WORKBENCH_Prepass *prepass = &wpd->prepass[transp][infront][hair];
DRWShadingGroup **grp_tex = NULL;
/* A hashmap stores image shgroups to pack all similar drawcalls together. */
if (BLI_ghash_ensure_p(prepass->material_hash, tex, (void ***)&grp_tex)) {
return *grp_tex;
}
DRWShadingGroup *grp = (tex_tile_data) ? prepass->image_tiled_shgrp : prepass->image_shgrp;
*grp_tex = grp = DRW_shgroup_create_sub(grp);
if (tex_tile_data) {
DRW_shgroup_uniform_texture_persistent(grp, "imageTileArray", tex);
DRW_shgroup_uniform_texture_persistent(grp, "imageTileData", tex_tile_data);
}
else {
DRW_shgroup_uniform_texture_persistent(grp, "imageTexture", tex);
}
DRW_shgroup_uniform_bool_copy(grp, "imagePremult", (ima && ima->alpha_mode == IMA_ALPHA_PREMUL));
DRW_shgroup_uniform_bool_copy(grp, "imageNearest", (interp == SHD_INTERP_CLOSEST));
return grp;
}

View File

@@ -0,0 +1,165 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2020, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Opaque Pieline:
*
* Use deferred shading to render opaque surfaces.
* This decouple the shading cost from scene complexity.
*
* The rendering is broken down in two passes:
* - the pre-pass where we render all the surfaces and output material data.
* - the composite pass where we compute the final aspect of the pixels.
*/
#include "DRW_render.h"
#include "GPU_extensions.h"
#include "workbench_engine.h"
#include "workbench_private.h"
void workbench_opaque_engine_init(WORKBENCH_Data *data)
{
WORKBENCH_FramebufferList *fbl = data->fbl;
WORKBENCH_PrivateData *wpd = data->stl->wpd;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DrawEngineType *owner = (DrawEngineType *)&workbench_opaque_engine_init;
/* Reused the same textures format for transparent pipeline to share the textures. */
const eGPUTextureFormat col_tex_format = GPU_RGBA16F;
const eGPUTextureFormat nor_tex_format = NORMAL_ENCODING_ENABLED() ? GPU_RG16F : GPU_RGBA16F;
wpd->material_buffer_tx = DRW_texture_pool_query_fullscreen(col_tex_format, owner);
wpd->normal_buffer_tx = DRW_texture_pool_query_fullscreen(nor_tex_format, owner);
GPU_framebuffer_ensure_config(&fbl->opaque_fb,
{
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
GPU_ATTACHMENT_TEXTURE(wpd->material_buffer_tx),
GPU_ATTACHMENT_TEXTURE(wpd->normal_buffer_tx),
GPU_ATTACHMENT_TEXTURE(wpd->object_id_tx),
});
}
void workbench_opaque_cache_init(WORKBENCH_Data *data)
{
WORKBENCH_PassList *psl = data->psl;
WORKBENCH_PrivateData *wpd = data->stl->wpd;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
struct GPUShader *sh;
DRWShadingGroup *grp;
const bool use_matcap = (wpd->shading.light == V3D_LIGHTING_MATCAP);
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
int opaque = 0;
for (int infront = 0; infront < 2; infront++) {
DRWPass *pass;
if (infront) {
DRW_PASS_CREATE(psl->opaque_infront_ps, state | wpd->cull_state | wpd->clip_state);
pass = psl->opaque_infront_ps;
}
else {
DRW_PASS_CREATE(psl->opaque_ps, state | wpd->cull_state | wpd->clip_state);
pass = psl->opaque_ps;
}
for (int hair = 0; hair < 2; hair++) {
wpd->prepass[opaque][infront][hair].material_hash = BLI_ghash_ptr_new(__func__);
sh = workbench_shader_opaque_get(wpd, hair);
wpd->prepass[opaque][infront][hair].common_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1);
wpd->prepass[opaque][infront][hair].vcol_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */
sh = workbench_shader_opaque_image_get(wpd, hair, false);
wpd->prepass[opaque][infront][hair].image_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
sh = workbench_shader_opaque_image_get(wpd, hair, true);
wpd->prepass[opaque][infront][hair].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
}
}
}
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_GREATER | DRW_STATE_STENCIL_EQUAL;
DRW_PASS_CREATE(psl->composite_ps, state);
sh = workbench_shader_composite_get(wpd);
grp = DRW_shgroup_create(sh, psl->composite_ps);
DRW_shgroup_uniform_block_persistent(grp, "world_block", wpd->world_ubo);
DRW_shgroup_uniform_texture_persistent(grp, "materialBuffer", wpd->material_buffer_tx);
DRW_shgroup_uniform_texture_persistent(grp, "normalBuffer", wpd->normal_buffer_tx);
DRW_shgroup_uniform_bool_copy(grp, "forceShadowing", false);
DRW_shgroup_stencil_mask(grp, 0x00);
if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) {
BKE_studiolight_ensure_flag(wpd->studio_light,
STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE |
STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE);
struct GPUTexture *diff_tx = wpd->studio_light->matcap_diffuse.gputexture;
struct GPUTexture *spec_tx = wpd->studio_light->matcap_specular.gputexture;
const bool use_spec = workbench_is_specular_highlight_enabled(wpd);
spec_tx = (use_spec && spec_tx) ? spec_tx : diff_tx;
DRW_shgroup_uniform_texture_persistent(grp, "matcapDiffuseImage", diff_tx);
DRW_shgroup_uniform_texture_persistent(grp, "matcapSpecularImage", spec_tx);
}
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
if (SHADOW_ENABLED(wpd)) {
grp = DRW_shgroup_create_sub(grp);
DRW_shgroup_uniform_bool_copy(grp, "forceShadowing", true);
DRW_shgroup_state_disable(grp, DRW_STATE_STENCIL_EQUAL);
DRW_shgroup_state_enable(grp, DRW_STATE_STENCIL_NEQUAL);
DRW_shgroup_stencil_mask(grp, 0x00);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}
{
DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_WRITE_STENCIL |
DRW_STATE_STENCIL_ALWAYS;
DRW_PASS_CREATE(psl->merge_infront_ps, state);
sh = workbench_shader_merge_infront_get(wpd);
grp = DRW_shgroup_create(sh, psl->merge_infront_ps);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth_in_front);
DRW_shgroup_stencil_mask(grp, 0x00);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}

View File

@@ -34,16 +34,14 @@
#include "workbench_engine.h"
#define WORKBENCH_ENGINE "BLENDER_WORKBENCH"
#define MAX_COMPOSITE_SHADERS (1 << 7)
#define MAX_PREPASS_SHADERS (1 << 8)
#define MAX_ACCUM_SHADERS (1 << 8)
#define MAX_CAVITY_SHADERS (1 << 3)
extern struct DrawEngineType draw_engine_workbench;
#define WORKBENCH_ENGINE "BLENDER_WORKBENCH"
#define MAX_MATERIAL (1 << 12)
#define DEBUG_SHADOW_VOLUME 0
#define TEXTURE_DRAWING_ENABLED(wpd) (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR)
#define VERTEX_COLORS_ENABLED(wpd) (wpd->shading.color_type == V3D_SHADING_VERTEX_COLOR)
#define MATERIAL_COLORS_ENABLED(wpd) (wpd->shading.color_type == V3D_SHADING_MATERIAL_COLOR)
#define FLAT_ENABLED(wpd) (wpd->shading.light == V3D_LIGHTING_FLAT)
#define STUDIOLIGHT_ENABLED(wpd) (wpd->shading.light == V3D_LIGHTING_STUDIO)
#define MATCAP_ENABLED(wpd) (wpd->shading.light == V3D_LIGHTING_MATCAP)
#define USE_WORLD_ORIENTATION(wpd) ((wpd->shading.flag & V3D_SHADING_WORLD_ORIENTATION) != 0)
@@ -63,45 +61,24 @@
(wpd->shading.cavity_type == V3D_SHADING_CAVITY_BOTH)))
#define CAVITY_ENABLED(wpd) (CURVATURE_ENABLED(wpd) || SSAO_ENABLED(wpd))
#define SHADOW_ENABLED(wpd) (wpd->shading.flag & V3D_SHADING_SHADOW)
#define GHOST_ENABLED(psl) \
(!DRW_pass_is_empty(psl->ghost_prepass_pass) || !DRW_pass_is_empty(psl->ghost_prepass_hair_pass))
#define CULL_BACKFACE_ENABLED(wpd) ((wpd->shading.flag & V3D_SHADING_BACKFACE_CULLING) != 0)
#define OIT_ENABLED(wpd) \
(ELEM(wpd->shading.color_type, \
V3D_SHADING_MATERIAL_COLOR, \
V3D_SHADING_OBJECT_COLOR, \
V3D_SHADING_TEXTURE_COLOR, \
V3D_SHADING_VERTEX_COLOR))
#define IS_NAVIGATING(wpd) \
((DRW_context_state_get()->rv3d) && \
(DRW_context_state_get()->rv3d->rflag & (RV3D_NAVIGATING | RV3D_PAINTING)))
#define OBJECT_OUTLINE_ENABLED(wpd) (wpd->shading.flag & V3D_SHADING_OBJECT_OUTLINE)
#define OBJECT_ID_PASS_ENABLED(wpd) (OBJECT_OUTLINE_ENABLED(wpd) || CURVATURE_ENABLED(wpd))
#define NORMAL_VIEWPORT_COMP_PASS_ENABLED(wpd) \
(MATCAP_ENABLED(wpd) || STUDIOLIGHT_ENABLED(wpd) || SHADOW_ENABLED(wpd))
#define NORMAL_VIEWPORT_PASS_ENABLED(wpd) \
(NORMAL_VIEWPORT_COMP_PASS_ENABLED(wpd) || SSAO_ENABLED(wpd) || CURVATURE_ENABLED(wpd))
#define NORMAL_ENCODING_ENABLED() (true)
#define WORLD_CLIPPING_ENABLED(wpd) (wpd->world_clip_planes != NULL)
struct RenderEngine;
struct RenderLayer;
struct rcti;
typedef struct WORKBENCH_FramebufferList {
/* Deferred render buffers */
struct GPUFrameBuffer *prepass_fb;
struct GPUFrameBuffer *ghost_prepass_fb;
struct GPUFrameBuffer *cavity_fb;
struct GPUFrameBuffer *composite_fb;
struct GPUFrameBuffer *id_clear_fb;
struct GPUFrameBuffer *opaque_fb;
struct GPUFrameBuffer *opaque_infront_fb;
struct GPUFrameBuffer *effect_fb;
struct GPUFrameBuffer *effect_taa_fb;
struct GPUFrameBuffer *depth_buffer_fb;
struct GPUFrameBuffer *color_only_fb;
struct GPUFrameBuffer *transp_accum_fb;
struct GPUFrameBuffer *transp_accum_infront_fb;
struct GPUFrameBuffer *id_clear_fb;
struct GPUFrameBuffer *dof_downsample_fb;
struct GPUFrameBuffer *dof_coc_tile_h_fb;
@@ -110,10 +87,9 @@ typedef struct WORKBENCH_FramebufferList {
struct GPUFrameBuffer *dof_blur1_fb;
struct GPUFrameBuffer *dof_blur2_fb;
/* Forward render buffers */
struct GPUFrameBuffer *object_outline_fb;
struct GPUFrameBuffer *transparent_accum_fb;
struct GPUFrameBuffer *transparent_revealage_fb;
struct GPUFrameBuffer *antialiasing_fb;
struct GPUFrameBuffer *smaa_edge_fb;
struct GPUFrameBuffer *smaa_weight_fb;
} WORKBENCH_FramebufferList;
typedef struct WORKBENCH_TextureList {
@@ -121,34 +97,35 @@ typedef struct WORKBENCH_TextureList {
struct GPUTexture *coc_halfres_tx;
struct GPUTexture *history_buffer_tx;
struct GPUTexture *depth_buffer_tx;
struct GPUTexture *smaa_search_tx;
struct GPUTexture *smaa_area_tx;
struct GPUTexture *dummy_image_tx;
struct GPUTexture *dummy_volume_tx;
struct GPUTexture *dummy_coba_tx;
} WORKBENCH_TextureList;
typedef struct WORKBENCH_StorageList {
struct WORKBENCH_PrivateData *g_data;
struct WORKBENCH_EffectInfo *effects;
struct WORKBENCH_PrivateData *wpd;
float *dof_ubo_data;
} WORKBENCH_StorageList;
typedef struct WORKBENCH_PassList {
/* deferred rendering */
struct DRWPass *prepass_pass;
struct DRWPass *prepass_hair_pass;
struct DRWPass *ghost_prepass_pass;
struct DRWPass *ghost_prepass_hair_pass;
struct DRWPass *cavity_pass;
struct DRWPass *shadow_depth_pass_pass;
struct DRWPass *shadow_depth_pass_mani_pass;
struct DRWPass *shadow_depth_fail_pass;
struct DRWPass *shadow_depth_fail_mani_pass;
struct DRWPass *shadow_depth_fail_caps_pass;
struct DRWPass *shadow_depth_fail_caps_mani_pass;
struct DRWPass *composite_pass;
struct DRWPass *composite_shadow_pass;
struct DRWPass *oit_composite_pass;
struct DRWPass *background_pass;
struct DRWPass *background_pass_clip;
struct DRWPass *ghost_resolve_pass;
struct DRWPass *effect_aa_pass;
struct DRWPass *opaque_ps;
struct DRWPass *opaque_infront_ps;
struct DRWPass *transp_resolve_ps;
struct DRWPass *transp_accum_ps;
struct DRWPass *transp_accum_infront_ps;
struct DRWPass *shadow_ps[2];
struct DRWPass *merge_infront_ps;
struct DRWPass *cavity_ps;
struct DRWPass *outline_ps;
struct DRWPass *composite_ps;
struct DRWPass *dof_down_ps;
struct DRWPass *dof_down2_ps;
struct DRWPass *dof_flatten_v_ps;
@@ -158,12 +135,13 @@ typedef struct WORKBENCH_PassList {
struct DRWPass *dof_blur1_ps;
struct DRWPass *dof_blur2_ps;
struct DRWPass *dof_resolve_ps;
struct DRWPass *volume_pass;
/* forward rendering */
struct DRWPass *transparent_accum_pass;
struct DRWPass *object_outline_pass;
struct DRWPass *depth_pass;
struct DRWPass *volume_ps;
struct DRWPass *aa_accum_ps;
struct DRWPass *aa_edge_ps;
struct DRWPass *aa_weight_ps;
struct DRWPass *aa_resolve_ps;
} WORKBENCH_PassList;
typedef struct WORKBENCH_Data {
@@ -180,84 +158,164 @@ typedef struct WORKBENCH_UBO_Light {
float diffuse_color[3], wrapped;
} WORKBENCH_UBO_Light;
typedef struct WORKBENCH_UBO_Material {
float base_color[3];
/* Packed data into a int. Decoded in the shader. */
uint32_t packed_data;
} WORKBENCH_UBO_Material;
typedef struct WORKBENCH_UBO_World {
float viewvecs[3][4];
float viewport_size[2], viewport_size_inv[2];
float object_outline_color[4];
float shadow_direction_vs[4];
float shadow_focus, shadow_shift, shadow_mul, shadow_add;
WORKBENCH_UBO_Light lights[4];
float ambient_color[4];
int num_lights;
int matcap_orientation;
int cavity_sample_start;
int cavity_sample_end;
float cavity_sample_count_inv;
float cavity_jitter_scale;
float cavity_valley_factor;
float cavity_ridge_factor;
float cavity_attenuation;
float cavity_distance;
float curvature_ridge;
float curvature_valley;
float ui_scale;
float _pad0;
int matcap_orientation;
int use_specular; /* Bools are 32bit ints in GLSL. */
int _pad1;
int _pad2;
} WORKBENCH_UBO_World;
BLI_STATIC_ASSERT_ALIGN(WORKBENCH_UBO_World, 16)
BLI_STATIC_ASSERT_ALIGN(WORKBENCH_UBO_Light, 16)
BLI_STATIC_ASSERT_ALIGN(WORKBENCH_UBO_Material, 16)
typedef struct WORKBENCH_Prepass {
/** Hash storing shading group for each Material or GPUTexture to reduce state changes. */
struct GHash *material_hash;
/** First common (non-vcol and non-image colored) shading group to created subgroups. */
struct DRWShadingGroup *common_shgrp;
/** First Vertex Color shading group to created subgroups. */
struct DRWShadingGroup *vcol_shgrp;
/** First Image shading group to created subgroups. */
struct DRWShadingGroup *image_shgrp;
/** First UDIM (tiled image) shading group to created subgroups. */
struct DRWShadingGroup *image_tiled_shgrp;
} WORKBENCH_Prepass;
typedef struct WORKBENCH_PrivateData {
struct GHash *material_hash;
struct GHash *material_transp_hash;
struct GPUShader *prepass_sh;
struct GPUShader *prepass_hair_sh;
struct GPUShader *prepass_uniform_sh;
struct GPUShader *prepass_uniform_hair_sh;
struct GPUShader *prepass_textured_sh;
struct GPUShader *prepass_textured_array_sh;
struct GPUShader *prepass_vertex_sh;
struct GPUShader *composite_sh;
struct GPUShader *background_sh;
struct GPUShader *transparent_accum_sh;
struct GPUShader *transparent_accum_hair_sh;
struct GPUShader *transparent_accum_uniform_sh;
struct GPUShader *transparent_accum_uniform_hair_sh;
struct GPUShader *transparent_accum_textured_sh;
struct GPUShader *transparent_accum_textured_array_sh;
struct GPUShader *transparent_accum_vertex_sh;
/** ViewLayerData for faster access. */
struct WORKBENCH_ViewLayerData *vldata;
/** Copy of draw_ctx->sh_cfg for faster access. */
eGPUShaderConfig sh_cfg;
/** Global clip and cull states. */
DRWState clip_state, cull_state;
/** Copy of scene->display.shading or v3d->shading for viewport. */
View3DShading shading;
/** Chosen studiolight or matcap. */
StudioLight *studio_light;
/** Copy of ctx_draw->scene for faster access. */
struct Scene *scene;
/** Shorthand version of U global for user preferences. */
const UserDef *preferences;
/* Does this instance owns the `world_ubo` field.
* Normally the field is borrowed from `WORKBENCH_WorldData`. In case that
* there is no World attached to the scene the UBO cannot be cached and should
* be freed after using. */
bool is_world_ubo_owner;
/** Copy of context mode for faster access. */
eContextObjectMode ctx_mode;
/** Shorthand for wpd->vldata->world_ubo. */
struct GPUUniformBuffer *world_ubo;
struct DRWShadingGroup *shadow_shgrp;
struct DRWShadingGroup *depth_shgrp;
WORKBENCH_UBO_World world_data;
float shadow_multiplier;
float shadow_shift;
float shadow_focus;
float cached_shadow_direction[3];
/** Background color to clear the color buffer with. */
float background_color[4];
/* Shadow */
/** Previous shadow direction to test if shadow has changed. */
float shadow_cached_direction[3];
/** Current shadow direction in world space. */
float shadow_direction_ws[3];
/** Shadow precomputed matrices. */
float shadow_mat[4][4];
float shadow_inv[4][4];
/* Far plane of the view frustum. */
/** Far plane of the view frustum. Used for shadow volume extrusion. */
float shadow_far_plane[4];
/* Near plane corners in shadow space. */
float shadow_near_corners[4][3];
/* min and max of shadow_near_corners. allow fast test */
/** Min and max of shadow_near_corners. Speed up culling test. */
float shadow_near_min[3];
float shadow_near_max[3];
/* This is a parallelogram, so only 2 normal and distance to the edges. */
/** This is a parallelogram, so only 2 normal and distance to the edges. */
float shadow_near_sides[2][4];
/* Shadow shading groups. First array elem is for non-manifold geom and second for manifold. */
struct DRWShadingGroup *shadow_pass_grp[2];
struct DRWShadingGroup *shadow_fail_grp[2];
struct DRWShadingGroup *shadow_fail_caps_grp[2];
/** If the shadow has changed direction and ob bboxes needs to be updated. */
bool shadow_changed;
bool is_playback;
float (*world_clip_planes)[4];
/* Temporal Antialiasing */
/** Total number of samples to after which TAA stops accumulating samples. */
int taa_sample_len;
/** Current TAA sample index in [0..taa_sample_len[ range. */
int taa_sample;
/** Inverse of taa_sample to divide the accumulation buffer. */
float taa_sample_inv;
/** If the view has been updated and TAA needs to be reset. */
bool view_updated;
/** View */
struct DRWView *view;
/** Last projection matrix to see if view is still valid. */
float last_mat[4][4];
/* Smart Morphological Anti-Aliasing */
/** Temp buffers to store edges and weights. */
struct GPUTexture *smaa_edge_tx;
struct GPUTexture *smaa_weight_tx;
/** Weight of the smaa pass. */
float smaa_mix_factor;
/** Opaque pipeline buffers. */
struct GPUTexture *material_buffer_tx;
struct GPUTexture *composite_buffer_tx;
struct GPUTexture *normal_buffer_tx;
/** Transparent pipeline buffers. */
struct GPUTexture *accum_buffer_tx;
struct GPUTexture *reveal_buffer_tx;
/** Object IDs buffer for curvature & outline. */
struct GPUTexture *object_id_tx;
/** Prepass infos for each draw types [transparent][infront][hair]. */
WORKBENCH_Prepass prepass[2][2][2];
/* Materials */
/** Copy of vldata->material_ubo for faster access. */
struct BLI_memblock *material_ubo;
/** Copy of vldata->material_ubo_data for faster access. */
struct BLI_memblock *material_ubo_data;
/** Current material chunk being filled by workbench_material_setup_ex(). */
WORKBENCH_UBO_Material *material_ubo_data_curr;
struct GPUUniformBuffer *material_ubo_curr;
/** Copy of txl->dummy_image_tx for faster access. */
struct GPUTexture *dummy_image_tx;
/** Total number of used material chunk. */
int material_chunk_count;
/** Index of current material chunk. */
int material_chunk_curr;
/** Index of current material inside the material chunk. Only for material coloring mode. */
int material_index;
/* Volumes */
bool volumes_do;
/** List of smoke domain textures to free after drawing. */
ListBase smoke_domains;
/* Ssao */
float winmat[4][4];
float viewvecs[3][4];
float ssao_params[4];
float ssao_settings[4];
/* Dof */
/* Depth of Field */
/** Depth of field temp buffers. */
struct GPUTexture *dof_blur_tx;
struct GPUTexture *coc_temp_tx;
struct GPUTexture *coc_tiles_tx[2];
struct GPUUniformBuffer *dof_ubo;
/** Depth of field parameters. */
float dof_aperturesize;
float dof_distance;
float dof_invsensorsize;
@@ -265,37 +323,15 @@ typedef struct WORKBENCH_PrivateData {
float dof_blades;
float dof_rotation;
float dof_ratio;
/** True if any volume needs to be rendered. */
bool volumes_do;
/** Convenience boolean. */
bool dof_enabled;
/* Color Management */
bool use_color_management;
bool use_color_render_settings;
bool is_playback;
bool is_navigating;
} WORKBENCH_PrivateData; /* Transient data */
typedef struct WORKBENCH_EffectInfo {
/** View */
struct DRWView *view;
/** Last projection matrix to see if view is still valid. */
float last_mat[4][4];
int jitter_index;
float taa_mix_factor;
bool view_updated;
} WORKBENCH_EffectInfo;
typedef struct WORKBENCH_MaterialData {
float base_color[3], metallic;
float roughness, alpha;
eV3DShadingColorType color_type;
int interp;
Image *ima;
ImageUser *iuser;
/* Linked shgroup for drawing */
DRWShadingGroup *shgrp;
/* forward rendering */
DRWShadingGroup *shgrp_object_outline;
} WORKBENCH_MaterialData;
typedef struct WORKBENCH_ObjectData {
DrawData dd;
@@ -307,20 +343,21 @@ typedef struct WORKBENCH_ObjectData {
bool shadow_bbox_dirty;
} WORKBENCH_ObjectData;
typedef struct WORKBENCH_WorldData {
DrawData dd;
/* The cached `GPUUniformBuffer`, that is reused between draw calls. */
typedef struct WORKBENCH_ViewLayerData {
/** Depth of field sample location array.*/
struct GPUUniformBuffer *dof_sample_ubo;
/** All constant data used for a render loop.*/
struct GPUUniformBuffer *world_ubo;
} WORKBENCH_WorldData;
/* Enumeration containing override options for base color rendering.
* This is used to during painting to force the base color to show what you are
* painting using the selected lighting model. */
typedef enum WORKBENCH_ColorOverride {
WORKBENCH_COLOR_OVERRIDE_OFF = 0,
WORKBENCH_COLOR_OVERRIDE_TEXTURE = CTX_MODE_PAINT_TEXTURE,
WORKBENCH_COLOR_OVERRIDE_VERTEX = CTX_MODE_PAINT_VERTEX,
} WORKBENCH_ColorOverride;
/** Cavity sample location array.*/
struct GPUUniformBuffer *cavity_sample_ubo;
/** Blue noise texture used to randomize the sampling of some effects.*/
struct GPUTexture *cavity_jitter_tx;
/** Materials ubos allocated in a memblock for easy bookeeping. */
struct BLI_memblock *material_ubo;
struct BLI_memblock *material_ubo_data;
/** Number of samples for which cavity_sample_ubo is valid. */
int cavity_sample_count;
} WORKBENCH_ViewLayerData;
/* inline helper functions */
BLI_INLINE bool workbench_is_specular_highlight_enabled(WORKBENCH_PrivateData *wpd)
@@ -333,234 +370,129 @@ BLI_INLINE bool workbench_is_specular_highlight_enabled(WORKBENCH_PrivateData *w
return false;
}
BLI_INLINE bool workbench_is_taa_enabled(WORKBENCH_PrivateData *wpd)
{
if (DRW_state_is_image_render()) {
const DRWContextState *draw_ctx = DRW_context_state_get();
if (draw_ctx->v3d) {
return draw_ctx->scene->display.viewport_aa > SCE_DISPLAY_AA_FXAA;
}
else {
return draw_ctx->scene->display.render_aa > SCE_DISPLAY_AA_FXAA;
}
}
else {
return !(IS_NAVIGATING(wpd) || wpd->is_playback) &&
wpd->preferences->viewport_aa > SCE_DISPLAY_AA_FXAA;
}
}
/* workbench_opaque.c */
void workbench_opaque_engine_init(WORKBENCH_Data *data);
void workbench_opaque_cache_init(WORKBENCH_Data *data);
BLI_INLINE bool workbench_is_fxaa_enabled(WORKBENCH_PrivateData *wpd)
{
if (DRW_state_is_image_render()) {
const DRWContextState *draw_ctx = DRW_context_state_get();
if (draw_ctx->v3d) {
return draw_ctx->scene->display.viewport_aa == SCE_DISPLAY_AA_FXAA;
}
else {
return draw_ctx->scene->display.render_aa == SCE_DISPLAY_AA_FXAA;
}
}
else {
if (wpd->preferences->viewport_aa == SCE_DISPLAY_AA_FXAA) {
return true;
}
/* workbench_transparent.c */
void workbench_transparent_engine_init(WORKBENCH_Data *data);
void workbench_transparent_cache_init(WORKBENCH_Data *data);
void workbench_transparent_draw_depth_pass(WORKBENCH_Data *data);
/* when navigating or animation playback use FXAA if scene uses TAA. */
return (IS_NAVIGATING(wpd) || wpd->is_playback) &&
wpd->preferences->viewport_aa > SCE_DISPLAY_AA_FXAA;
}
}
/* workbench_shadow.c */
void workbench_shadow_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd);
void workbench_shadow_cache_init(WORKBENCH_Data *data);
void workbench_shadow_cache_populate(WORKBENCH_Data *data, Object *ob, const bool has_transp_mat);
/** Is texture paint mode enabled (globally) */
BLI_INLINE bool workbench_is_in_texture_paint_mode(void)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
return draw_ctx->object_mode == OB_MODE_TEXTURE_PAINT;
}
/* workbench_shader.c */
GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, bool hair);
GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd, bool hair, bool tiled);
GPUShader *workbench_shader_composite_get(WORKBENCH_PrivateData *wpd);
GPUShader *workbench_shader_merge_infront_get(WORKBENCH_PrivateData *wpd);
/** Is vertex paint mode enabled (globally) */
BLI_INLINE bool workbench_is_in_vertex_paint_mode(void)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
return draw_ctx->object_mode == OB_MODE_VERTEX_PAINT;
}
GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd, bool hair);
GPUShader *workbench_shader_transparent_image_get(WORKBENCH_PrivateData *wpd,
bool hair,
bool tiled);
GPUShader *workbench_shader_transparent_resolve_get(WORKBENCH_PrivateData *wpd);
/* Must the `View3DShading.color_type` be overriden for the given object. */
BLI_INLINE WORKBENCH_ColorOverride workbench_object_color_override_get(Object *ob)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
if (ob->type == OB_MESH && (draw_ctx->obact == ob)) {
const enum eContextObjectMode mode = CTX_data_mode_enum_ex(
draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode);
if (mode == CTX_MODE_PAINT_TEXTURE) {
return WORKBENCH_COLOR_OVERRIDE_TEXTURE;
}
else if (mode == CTX_MODE_PAINT_VERTEX) {
return WORKBENCH_COLOR_OVERRIDE_VERTEX;
}
}
GPUShader *workbench_shader_shadow_pass_get(bool manifold);
GPUShader *workbench_shader_shadow_fail_get(bool manifold, bool cap);
return WORKBENCH_COLOR_OVERRIDE_OFF;
}
GPUShader *workbench_shader_cavity_get(bool cavity, bool curvature);
GPUShader *workbench_shader_outline_get(void);
BLI_INLINE bool workbench_is_matdata_pass_enabled(WORKBENCH_PrivateData *wpd)
{
return (wpd->shading.color_type != V3D_SHADING_SINGLE_COLOR || MATCAP_ENABLED(wpd)) ||
workbench_is_in_texture_paint_mode() || workbench_is_in_vertex_paint_mode();
}
GPUShader *workbench_shader_antialiasing_accumulation_get(void);
GPUShader *workbench_shader_antialiasing_get(int stage);
/**
* Get the default texture format to be used by the color and history buffers.
*
* Use GPU_RGBA16F for final renderings and for drawing textures. This
* allows displaying HDRI textures. Vertex Colors uses GPU_RGBA16 to resolve
* color banding issues (T66100). All other modes use GPU_RGBA8 to reduce
* bandwidth and gpu memory.
*/
BLI_INLINE eGPUTextureFormat workbench_color_texture_format(const WORKBENCH_PrivateData *wpd)
{
eGPUTextureFormat result;
if (DRW_state_is_image_render() || workbench_is_in_texture_paint_mode() ||
TEXTURE_DRAWING_ENABLED(wpd)) {
result = GPU_RGBA16F;
}
else {
result = GPU_RGBA16;
}
return result;
}
GPUShader *workbench_shader_volume_get(bool slice, bool coba, bool cubic);
/* workbench_deferred.c */
void workbench_deferred_engine_init(WORKBENCH_Data *vedata);
void workbench_deferred_engine_free(void);
void workbench_deferred_draw_scene(WORKBENCH_Data *vedata);
void workbench_deferred_draw_finish(WORKBENCH_Data *vedata);
void workbench_deferred_cache_init(WORKBENCH_Data *vedata);
void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob);
void workbench_deferred_cache_finish(WORKBENCH_Data *vedata);
void workbench_shader_depth_of_field_get(GPUShader **prepare_sh,
GPUShader **downsample_sh,
GPUShader **blur1_sh,
GPUShader **blur2_sh,
GPUShader **resolve_sh);
/* workbench_forward.c */
void workbench_forward_engine_init(WORKBENCH_Data *vedata);
void workbench_forward_engine_free(void);
void workbench_forward_draw_scene(WORKBENCH_Data *vedata);
void workbench_forward_draw_finish(WORKBENCH_Data *vedata);
void workbench_forward_cache_init(WORKBENCH_Data *vedata);
void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob);
void workbench_forward_cache_finish(WORKBENCH_Data *vedata);
void workbench_shader_library_ensure(void);
void workbench_shader_free(void);
/* For OIT in deferred */
void workbench_forward_outline_shaders_ensure(WORKBENCH_PrivateData *wpd, eGPUShaderConfig sh_cfg);
void workbench_forward_choose_shaders(WORKBENCH_PrivateData *wpd, eGPUShaderConfig sh_cfg);
WORKBENCH_MaterialData *workbench_forward_get_or_create_material_data(
WORKBENCH_Data *vedata,
Object *ob,
Material *mat,
Image *ima,
ImageUser *iuser,
eV3DShadingColorType color_type,
int interp);
/* workbench_effect_antialiasing.c */
int workbench_antialiasing_sample_count_get(WORKBENCH_PrivateData *wpd);
void workbench_antialiasing_engine_init(WORKBENCH_Data *vedata);
void workbench_antialiasing_cache_init(WORKBENCH_Data *vedata);
void workbench_antialiasing_view_updated(WORKBENCH_Data *vedata);
bool workbench_antialiasing_setup(WORKBENCH_Data *vedata);
void workbench_antialiasing_draw_pass(WORKBENCH_Data *vedata);
/* workbench_effect_aa.c */
void workbench_aa_create_pass(WORKBENCH_Data *vedata, GPUTexture **tx);
void workbench_aa_draw_pass(WORKBENCH_Data *vedata, GPUTexture *tx);
/* workbench_effect_fxaa.c */
void workbench_fxaa_engine_init(void);
void workbench_fxaa_engine_free(void);
DRWPass *workbench_fxaa_create_pass(GPUTexture **color_buffer_tx);
/* workbench_effect_taa.c */
void workbench_taa_engine_init(WORKBENCH_Data *vedata);
void workbench_taa_engine_free(void);
DRWPass *workbench_taa_create_pass(WORKBENCH_Data *vedata, GPUTexture **color_buffer_tx);
void workbench_taa_draw_scene_start(WORKBENCH_Data *vedata);
void workbench_taa_draw_scene_end(WORKBENCH_Data *vedata);
void workbench_taa_view_updated(WORKBENCH_Data *vedata);
int workbench_taa_calculate_num_iterations(WORKBENCH_Data *vedata);
int workbench_num_viewport_rendering_iterations(WORKBENCH_Data *vedata);
/* workbench_effect_cavity.c */
void workbench_cavity_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd);
void workbench_cavity_samples_ubo_ensure(WORKBENCH_PrivateData *wpd);
void workbench_cavity_cache_init(WORKBENCH_Data *data);
/* workbench_effect_outline.c */
void workbench_outline_cache_init(WORKBENCH_Data *data);
/* workbench_effect_dof.c */
void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera);
void workbench_dof_engine_free(void);
void workbench_dof_create_pass(WORKBENCH_Data *vedata,
GPUTexture **dof_input,
GPUTexture *noise_tex);
void workbench_dof_engine_init(WORKBENCH_Data *vedata);
void workbench_dof_cache_init(WORKBENCH_Data *vedata);
void workbench_dof_draw_pass(WORKBENCH_Data *vedata);
/* workbench_materials.c */
eV3DShadingColorType workbench_material_determine_color_type(WORKBENCH_PrivateData *wpd,
Image *ima,
Object *ob,
bool use_sculpt_pbvh);
void workbench_material_get_image_and_mat(
Object *ob, int mat_nr, Image **r_image, ImageUser **r_iuser, int *r_interp, Material **r_mat);
char *workbench_material_build_defines(WORKBENCH_PrivateData *wpd,
bool is_uniform_color,
bool is_hair,
bool is_tiled,
const WORKBENCH_ColorOverride color_override);
void workbench_material_update_data(WORKBENCH_PrivateData *wpd,
Object *ob,
Material *mat,
WORKBENCH_MaterialData *data,
int color_type);
uint workbench_material_get_hash(WORKBENCH_MaterialData *material_template, bool is_ghost);
int workbench_material_get_composite_shader_index(WORKBENCH_PrivateData *wpd);
int workbench_material_get_prepass_shader_index(WORKBENCH_PrivateData *wpd,
bool is_uniform_color,
bool is_hair,
bool is_tiled,
const WORKBENCH_ColorOverride color_override);
int workbench_material_get_accum_shader_index(WORKBENCH_PrivateData *wpd,
bool is_uniform_color,
bool is_hair,
bool is_tiled,
const WORKBENCH_ColorOverride color_override);
void workbench_material_shgroup_uniform(WORKBENCH_PrivateData *wpd,
DRWShadingGroup *grp,
WORKBENCH_MaterialData *material,
Object *ob,
const bool deferred,
const bool is_tiled,
const int interp);
void workbench_material_copy(WORKBENCH_MaterialData *dest_material,
const WORKBENCH_MaterialData *source_material);
void workbench_material_ubo_data(WORKBENCH_PrivateData *wpd,
Object *ob,
Material *mat,
WORKBENCH_UBO_Material *data,
eV3DShadingColorType color_type);
/* workbench_studiolight.c */
void studiolight_update_world(WORKBENCH_PrivateData *wpd,
StudioLight *sl,
WORKBENCH_UBO_World *wd);
void studiolight_update_light(WORKBENCH_PrivateData *wpd, const float light_direction[3]);
bool studiolight_object_cast_visible_shadow(WORKBENCH_PrivateData *wpd,
Object *ob,
WORKBENCH_ObjectData *oed);
float studiolight_object_shadow_distance(WORKBENCH_PrivateData *wpd,
Object *ob,
WORKBENCH_ObjectData *oed);
bool studiolight_camera_in_object_shadow(WORKBENCH_PrivateData *wpd,
Object *ob,
WORKBENCH_ObjectData *oed);
DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd,
Object *ob,
int mat_nr,
eV3DShadingColorType color_type,
bool hair,
bool *r_transp);
DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd,
Object *ob,
int mat_nr,
Image *ima,
ImageUser *iuser,
int interp,
bool hair);
#define workbench_material_setup(wpd, ob, mat_nr, color_type, r_transp) \
workbench_material_setup_ex(wpd, ob, mat_nr, color_type, false, r_transp)
#define workbench_image_setup(wpd, ob, mat_nr, ima, iuser, interp) \
workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, interp, false)
#define workbench_material_hair_setup(wpd, ob, mat_nr, color_type) \
workbench_material_setup_ex(wpd, ob, mat_nr, color_type, true, 0)
#define workbench_image_hair_setup(wpd, ob, mat_nr, ima, iuser, interp) \
workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, interp, true)
/* workbench_data.c */
void workbench_effect_info_init(WORKBENCH_EffectInfo *effect_info);
void workbench_private_data_init(WORKBENCH_PrivateData *wpd);
void workbench_private_data_free(WORKBENCH_PrivateData *wpd);
void workbench_private_data_get_light_direction(float r_light_direction[3]);
void workbench_clear_color_get(float color[4]);
void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd);
void workbench_update_material_ubos(WORKBENCH_PrivateData *wpd);
struct GPUUniformBuffer *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd);
/* workbench_volume.c */
void workbench_volume_engine_init(void);
void workbench_volume_engine_free(void);
void workbench_volume_engine_init(WORKBENCH_Data *vedata);
void workbench_volume_cache_init(WORKBENCH_Data *vedata);
void workbench_volume_cache_populate(WORKBENCH_Data *vedata,
Scene *scene,
Object *ob,
struct Scene *UNUSED(scene),
struct Object *ob,
struct ModifierData *md);
void workbench_volume_smoke_textures_free(WORKBENCH_PrivateData *wpd);
void workbench_volume_draw_pass(WORKBENCH_Data *vedata);
void workbench_volume_draw_finish(WORKBENCH_Data *vedata);
/* workbench_engine.c */
void workbench_engine_init(void *ved);
void workbench_cache_init(void *ved);
void workbench_cache_populate(void *ved, Object *ob);
void workbench_cache_finish(void *ved);
void workbench_draw_sample(void *ved);
void workbench_draw_finish(void *ved);
/* workbench_render.c */
void workbench_render(WORKBENCH_Data *vedata,
void workbench_render(void *ved,
struct RenderEngine *engine,
struct RenderLayer *render_layer,
const struct rcti *rect);

View File

@@ -41,20 +41,12 @@
#include "workbench_private.h"
static void workbench_render_deferred_cache(void *vedata,
struct Object *ob,
struct RenderEngine *UNUSED(engine),
struct Depsgraph *UNUSED(depsgraph))
static void workbench_render_cache(void *vedata,
struct Object *ob,
struct RenderEngine *UNUSED(engine),
struct Depsgraph *UNUSED(depsgraph))
{
workbench_deferred_solid_cache_populate(vedata, ob);
}
static void workbench_render_forward_cache(void *vedata,
struct Object *ob,
struct RenderEngine *UNUSED(engine),
struct Depsgraph *UNUSED(depsgraph))
{
workbench_forward_cache_populate(vedata, ob);
workbench_cache_populate(vedata, ob);
}
static void workbench_render_matrices_init(RenderEngine *engine, Depsgraph *depsgraph)
@@ -171,18 +163,11 @@ static void workbench_render_result_z(struct RenderLayer *rl,
}
}
static void workbench_render_framebuffers_finish(void)
{
}
void workbench_render(WORKBENCH_Data *data,
RenderEngine *engine,
RenderLayer *render_layer,
const rcti *rect)
void workbench_render(void *ved, RenderEngine *engine, RenderLayer *render_layer, const rcti *rect)
{
WORKBENCH_Data *data = ved;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
Depsgraph *depsgraph = draw_ctx->depsgraph;
workbench_render_matrices_init(engine, depsgraph);
@@ -191,59 +176,31 @@ void workbench_render(WORKBENCH_Data *data,
return;
}
const bool deferred = !XRAY_FLAG_ENABLED(&scene->display);
workbench_engine_init(data);
if (deferred) {
/* Init engine. */
workbench_deferred_engine_init(data);
workbench_cache_init(data);
DRW_render_object_iter(data, engine, depsgraph, workbench_render_cache);
workbench_cache_finish(data);
/* Init objects. */
workbench_deferred_cache_init(data);
DRW_render_object_iter(data, engine, depsgraph, workbench_render_deferred_cache);
workbench_deferred_cache_finish(data);
DRW_render_instance_buffer_finish();
DRW_render_instance_buffer_finish();
/* Also we weed to have a correct fbo bound for DRW_hair_update */
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_hair_update();
/* Also we weed to have a correct fbo bound for DRW_hair_update */
GPU_framebuffer_bind(dfbl->default_fb);
DRW_hair_update();
/* Draw. */
int num_samples = workbench_taa_calculate_num_iterations(data);
for (int sample = 0; sample < num_samples; sample++) {
if (RE_engine_test_break(engine)) {
break;
}
workbench_deferred_draw_scene(data);
GPU_framebuffer_bind(dfbl->default_fb);
GPU_framebuffer_clear_depth(dfbl->default_fb, 1.0f);
WORKBENCH_PrivateData *wpd = data->stl->wpd;
while (wpd->taa_sample < max_ii(1, wpd->taa_sample_len)) {
if (RE_engine_test_break(engine)) {
break;
}
workbench_deferred_draw_finish(data);
workbench_update_world_ubo(wpd);
workbench_draw_sample(data);
}
else {
/* Init engine. */
workbench_forward_engine_init(data);
/* Init objects. */
workbench_forward_cache_init(data);
DRW_render_object_iter(data, engine, depsgraph, workbench_render_forward_cache);
workbench_forward_cache_finish(data);
DRW_render_instance_buffer_finish();
/* Also we weed to have a correct fbo bound for DRW_hair_update */
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_hair_update();
/* Draw. */
int num_samples = workbench_taa_calculate_num_iterations(data);
for (int sample = 0; sample < num_samples; sample++) {
if (RE_engine_test_break(engine)) {
break;
}
workbench_forward_draw_scene(data);
}
workbench_forward_draw_finish(data);
}
workbench_draw_finish(data);
/* Write render output. */
const char *viewname = RE_GetActiveRenderView(engine->re);
@@ -260,8 +217,6 @@ void workbench_render(WORKBENCH_Data *data,
rp->rect);
workbench_render_result_z(render_layer, viewname, rect);
workbench_render_framebuffers_finish();
}
void workbench_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)

View File

@@ -0,0 +1,533 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2020, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
#include "DRW_render.h"
#include "BLI_dynstr.h"
#include "workbench_engine.h"
#include "workbench_private.h"
extern char datatoc_common_hair_lib_glsl[];
extern char datatoc_common_view_lib_glsl[];
extern char datatoc_common_smaa_lib_glsl[];
extern char datatoc_workbench_prepass_vert_glsl[];
extern char datatoc_workbench_prepass_hair_vert_glsl[];
extern char datatoc_workbench_prepass_frag_glsl[];
extern char datatoc_workbench_effect_cavity_frag_glsl[];
extern char datatoc_workbench_effect_outline_frag_glsl[];
extern char datatoc_workbench_effect_dof_frag_glsl[];
extern char datatoc_workbench_effect_taa_frag_glsl[];
extern char datatoc_workbench_effect_smaa_frag_glsl[];
extern char datatoc_workbench_effect_smaa_vert_glsl[];
extern char datatoc_workbench_composite_frag_glsl[];
extern char datatoc_workbench_transparent_accum_frag_glsl[];
extern char datatoc_workbench_transparent_resolve_frag_glsl[];
extern char datatoc_workbench_merge_infront_frag_glsl[];
extern char datatoc_workbench_shadow_vert_glsl[];
extern char datatoc_workbench_shadow_geom_glsl[];
extern char datatoc_workbench_shadow_caps_geom_glsl[];
extern char datatoc_workbench_shadow_debug_frag_glsl[];
extern char datatoc_workbench_volume_vert_glsl[];
extern char datatoc_workbench_volume_frag_glsl[];
extern char datatoc_workbench_cavity_lib_glsl[];
extern char datatoc_workbench_common_lib_glsl[];
extern char datatoc_workbench_curvature_lib_glsl[];
extern char datatoc_workbench_data_lib_glsl[];
extern char datatoc_workbench_image_lib_glsl[];
extern char datatoc_workbench_matcap_lib_glsl[];
extern char datatoc_workbench_material_lib_glsl[];
extern char datatoc_workbench_shader_interface_lib_glsl[];
extern char datatoc_workbench_world_light_lib_glsl[];
extern char datatoc_gpu_shader_depth_only_frag_glsl[];
extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
/* Maximum number of variations. */
#define MAX_LIGHTING 3
#define MAX_COLOR 3
#define MAX_GEOM 2
enum {
VOLUME_SH_SLICE = 0,
VOLUME_SH_COBA,
VOLUME_SH_CUBIC,
};
#define VOLUME_SH_MAX (1 << (VOLUME_SH_CUBIC + 1))
static struct {
struct GPUShader *opaque_prepass_sh_cache[GPU_SHADER_CFG_LEN][MAX_GEOM][MAX_COLOR];
struct GPUShader *transp_prepass_sh_cache[GPU_SHADER_CFG_LEN][MAX_GEOM][MAX_LIGHTING][MAX_COLOR];
struct GPUShader *opaque_composite_sh[MAX_LIGHTING];
struct GPUShader *oit_resolve_sh;
struct GPUShader *outline_sh;
struct GPUShader *merge_infront_sh;
struct GPUShader *shadow_depth_pass_sh[2];
struct GPUShader *shadow_depth_fail_sh[2][2];
struct GPUShader *cavity_sh[2][2];
struct GPUShader *dof_prepare_sh;
struct GPUShader *dof_downsample_sh;
struct GPUShader *dof_blur1_sh;
struct GPUShader *dof_blur2_sh;
struct GPUShader *dof_resolve_sh;
struct GPUShader *aa_accum_sh;
struct GPUShader *smaa_sh[3];
struct GPUShader *volume_sh[2][2][2];
struct DRWShaderLibrary *lib;
} e_data = {{{{NULL}}}};
void workbench_shader_library_ensure(void)
{
if (e_data.lib == NULL) {
e_data.lib = DRW_shader_library_create();
/* NOTE: Theses needs to be ordered by dependencies. */
DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib);
DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib);
DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib);
DRW_SHADER_LIB_ADD(e_data.lib, workbench_shader_interface_lib);
DRW_SHADER_LIB_ADD(e_data.lib, workbench_common_lib);
DRW_SHADER_LIB_ADD(e_data.lib, workbench_image_lib);
DRW_SHADER_LIB_ADD(e_data.lib, workbench_material_lib);
DRW_SHADER_LIB_ADD(e_data.lib, workbench_data_lib);
DRW_SHADER_LIB_ADD(e_data.lib, workbench_matcap_lib);
DRW_SHADER_LIB_ADD(e_data.lib, workbench_cavity_lib);
DRW_SHADER_LIB_ADD(e_data.lib, workbench_curvature_lib);
DRW_SHADER_LIB_ADD(e_data.lib, workbench_world_light_lib);
}
}
static char *workbench_build_defines(
WORKBENCH_PrivateData *wpd, bool textured, bool tiled, bool cavity, bool curvature)
{
char *str = NULL;
DynStr *ds = BLI_dynstr_new();
if (wpd && wpd->shading.light == V3D_LIGHTING_STUDIO) {
BLI_dynstr_append(ds, "#define V3D_LIGHTING_STUDIO\n");
}
else if (wpd && wpd->shading.light == V3D_LIGHTING_MATCAP) {
BLI_dynstr_append(ds, "#define V3D_LIGHTING_MATCAP\n");
}
else {
BLI_dynstr_append(ds, "#define V3D_LIGHTING_FLAT\n");
}
if (NORMAL_ENCODING_ENABLED()) {
BLI_dynstr_append(ds, "#define WORKBENCH_ENCODE_NORMALS\n");
}
if (textured) {
BLI_dynstr_append(ds, "#define V3D_SHADING_TEXTURE_COLOR\n");
}
if (tiled) {
BLI_dynstr_append(ds, "#define TEXTURE_IMAGE_ARRAY\n");
}
if (cavity) {
BLI_dynstr_append(ds, "#define USE_CAVITY\n");
}
if (curvature) {
BLI_dynstr_append(ds, "#define USE_CURVATURE\n");
}
str = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
return str;
}
static int workbench_color_index(WORKBENCH_PrivateData *UNUSED(wpd), bool textured, bool tiled)
{
BLI_assert(2 < MAX_COLOR);
return (textured) ? (tiled ? 2 : 1) : 0;
}
static GPUShader *workbench_shader_get_ex(
WORKBENCH_PrivateData *wpd, bool transp, bool hair, bool textured, bool tiled)
{
int color = workbench_color_index(wpd, textured, tiled);
int light = wpd->shading.light;
BLI_assert(light < MAX_LIGHTING);
struct GPUShader **shader =
(transp) ? &e_data.transp_prepass_sh_cache[wpd->sh_cfg][hair][light][color] :
&e_data.opaque_prepass_sh_cache[wpd->sh_cfg][hair][color];
if (*shader == NULL) {
char *defines = workbench_build_defines(wpd, textured, tiled, false, false);
char *frag_file = transp ? datatoc_workbench_transparent_accum_frag_glsl :
datatoc_workbench_prepass_frag_glsl;
char *frag_src = DRW_shader_library_create_shader_string(e_data.lib, frag_file);
char *vert_file = hair ? datatoc_workbench_prepass_hair_vert_glsl :
datatoc_workbench_prepass_vert_glsl;
char *vert_src = DRW_shader_library_create_shader_string(e_data.lib, vert_file);
const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[wpd->sh_cfg];
*shader = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg_data->lib, vert_src, NULL},
.frag = (const char *[]){frag_src, NULL},
.defs = (const char *[]){sh_cfg_data->def,
defines,
transp ? "#define TRANSPARENT_MATERIAL\n" :
"#define OPAQUE_MATERIAL\n",
NULL},
});
MEM_freeN(defines);
MEM_freeN(frag_src);
MEM_freeN(vert_src);
}
return *shader;
}
GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, bool hair)
{
return workbench_shader_get_ex(wpd, false, hair, false, false);
}
GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd, bool hair, bool tiled)
{
return workbench_shader_get_ex(wpd, false, hair, true, tiled);
}
GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd, bool hair)
{
return workbench_shader_get_ex(wpd, true, hair, false, false);
}
GPUShader *workbench_shader_transparent_image_get(WORKBENCH_PrivateData *wpd,
bool hair,
bool tiled)
{
return workbench_shader_get_ex(wpd, true, hair, true, tiled);
}
GPUShader *workbench_shader_composite_get(WORKBENCH_PrivateData *wpd)
{
int light = wpd->shading.light;
struct GPUShader **shader = &e_data.opaque_composite_sh[light];
BLI_assert(light < MAX_LIGHTING);
if (*shader == NULL) {
char *defines = workbench_build_defines(wpd, false, false, false, false);
char *frag = DRW_shader_library_create_shader_string(e_data.lib,
datatoc_workbench_composite_frag_glsl);
*shader = DRW_shader_create_fullscreen(frag, defines);
MEM_freeN(defines);
MEM_freeN(frag);
}
return *shader;
}
GPUShader *workbench_shader_merge_infront_get(WORKBENCH_PrivateData *UNUSED(wpd))
{
if (e_data.merge_infront_sh == NULL) {
char *frag = DRW_shader_library_create_shader_string(
e_data.lib, datatoc_workbench_merge_infront_frag_glsl);
e_data.merge_infront_sh = DRW_shader_create_fullscreen(frag, NULL);
MEM_freeN(frag);
}
return e_data.merge_infront_sh;
}
GPUShader *workbench_shader_transparent_resolve_get(WORKBENCH_PrivateData *wpd)
{
if (e_data.oit_resolve_sh == NULL) {
char *defines = workbench_build_defines(wpd, false, false, false, false);
e_data.oit_resolve_sh = DRW_shader_create_fullscreen(
datatoc_workbench_transparent_resolve_frag_glsl, defines);
MEM_freeN(defines);
}
return e_data.oit_resolve_sh;
}
static GPUShader *workbench_shader_shadow_pass_get_ex(bool depth_pass, bool manifold, bool cap)
{
struct GPUShader **shader = (depth_pass) ? &e_data.shadow_depth_pass_sh[manifold] :
&e_data.shadow_depth_fail_sh[manifold][cap];
if (*shader == NULL) {
#if DEBUG_SHADOW_VOLUME
const char *shadow_frag = datatoc_workbench_shadow_debug_frag_glsl;
#else
const char *shadow_frag = datatoc_gpu_shader_depth_only_frag_glsl;
#endif
*shader = GPU_shader_create_from_arrays({
.vert = (const char *[]){datatoc_common_view_lib_glsl,
datatoc_workbench_shadow_vert_glsl,
NULL},
.geom = (const char *[]){(cap) ? datatoc_workbench_shadow_caps_geom_glsl :
datatoc_workbench_shadow_geom_glsl,
NULL},
.frag = (const char *[]){shadow_frag, NULL},
.defs = (const char *[]){(depth_pass) ? "#define SHADOW_PASS\n" : "#define SHADOW_FAIL\n",
(manifold) ? "" : "#define DOUBLE_MANIFOLD\n",
NULL},
});
}
return *shader;
}
GPUShader *workbench_shader_shadow_pass_get(bool manifold)
{
return workbench_shader_shadow_pass_get_ex(true, manifold, false);
}
GPUShader *workbench_shader_shadow_fail_get(bool manifold, bool cap)
{
return workbench_shader_shadow_pass_get_ex(false, manifold, cap);
}
GPUShader *workbench_shader_cavity_get(bool cavity, bool curvature)
{
BLI_assert(cavity || curvature);
struct GPUShader **shader = &e_data.cavity_sh[cavity][curvature];
if (*shader == NULL) {
char *defines = workbench_build_defines(NULL, false, false, cavity, curvature);
char *frag = DRW_shader_library_create_shader_string(
e_data.lib, datatoc_workbench_effect_cavity_frag_glsl);
*shader = DRW_shader_create_fullscreen(frag, defines);
MEM_freeN(defines);
MEM_freeN(frag);
}
return *shader;
}
GPUShader *workbench_shader_outline_get(void)
{
if (e_data.outline_sh == NULL) {
char *frag = DRW_shader_library_create_shader_string(
e_data.lib, datatoc_workbench_effect_outline_frag_glsl);
e_data.outline_sh = DRW_shader_create_fullscreen(frag, NULL);
MEM_freeN(frag);
}
return e_data.outline_sh;
}
void workbench_shader_depth_of_field_get(GPUShader **prepare_sh,
GPUShader **downsample_sh,
GPUShader **blur1_sh,
GPUShader **blur2_sh,
GPUShader **resolve_sh)
{
if (e_data.dof_prepare_sh == NULL) {
e_data.dof_prepare_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl,
"#define PREPARE\n");
e_data.dof_downsample_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl,
"#define DOWNSAMPLE\n");
#if 0 /* TODO(fclem) finish COC min_max optimization */
e_data.dof_flatten_v_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl,
"#define FLATTEN_VERTICAL\n");
e_data.dof_flatten_h_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl,
"#define FLATTEN_HORIZONTAL\n");
e_data.dof_dilate_v_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl,
"#define DILATE_VERTICAL\n");
e_data.dof_dilate_h_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl,
"#define DILATE_HORIZONTAL\n");
#endif
e_data.dof_blur1_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl,
"#define BLUR1\n");
e_data.dof_blur2_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl,
"#define BLUR2\n");
e_data.dof_resolve_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl,
"#define RESOLVE\n");
}
*prepare_sh = e_data.dof_prepare_sh;
*downsample_sh = e_data.dof_downsample_sh;
*blur1_sh = e_data.dof_blur1_sh;
*blur2_sh = e_data.dof_blur2_sh;
*resolve_sh = e_data.dof_resolve_sh;
}
GPUShader *workbench_shader_antialiasing_accumulation_get(void)
{
if (e_data.aa_accum_sh == NULL) {
char *frag = DRW_shader_library_create_shader_string(e_data.lib,
datatoc_workbench_effect_taa_frag_glsl);
e_data.aa_accum_sh = DRW_shader_create_fullscreen(frag, NULL);
MEM_freeN(frag);
}
return e_data.aa_accum_sh;
}
GPUShader *workbench_shader_antialiasing_get(int stage)
{
BLI_assert(stage < 3);
if (!e_data.smaa_sh[stage]) {
char stage_define[32];
BLI_snprintf(stage_define, sizeof(stage_define), "#define SMAA_STAGE %d\n", stage);
e_data.smaa_sh[stage] = GPU_shader_create_from_arrays({
.vert =
(const char *[]){
"#define SMAA_INCLUDE_VS 1\n",
"#define SMAA_INCLUDE_PS 0\n",
"uniform vec4 viewportMetrics;\n",
datatoc_common_smaa_lib_glsl,
datatoc_workbench_effect_smaa_vert_glsl,
NULL,
},
.frag =
(const char *[]){
"#define SMAA_INCLUDE_VS 0\n",
"#define SMAA_INCLUDE_PS 1\n",
"uniform vec4 viewportMetrics;\n",
datatoc_common_smaa_lib_glsl,
datatoc_workbench_effect_smaa_frag_glsl,
NULL,
},
.defs =
(const char *[]){
"#define SMAA_GLSL_3\n",
"#define SMAA_RT_METRICS viewportMetrics\n",
"#define SMAA_PRESET_HIGH\n",
"#define SMAA_LUMA_WEIGHT float4(1.0, 1.0, 1.0, 1.0)\n",
"#define SMAA_NO_DISCARD\n",
stage_define,
NULL,
},
});
}
return e_data.smaa_sh[stage];
}
GPUShader *workbench_shader_volume_get(bool slice, bool coba, bool cubic)
{
GPUShader **shader = &e_data.volume_sh[slice][coba][cubic];
if (*shader == NULL) {
DynStr *ds = BLI_dynstr_new();
if (slice) {
BLI_dynstr_append(ds, "#define VOLUME_SLICE\n");
}
if (coba) {
BLI_dynstr_append(ds, "#define USE_COBA\n");
}
if (cubic) {
BLI_dynstr_append(ds, "#define USE_TRICUBIC\n");
}
char *defines = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
char *vert = DRW_shader_library_create_shader_string(e_data.lib,
datatoc_workbench_volume_vert_glsl);
char *frag = DRW_shader_library_create_shader_string(e_data.lib,
datatoc_workbench_volume_frag_glsl);
*shader = DRW_shader_create(vert, NULL, frag, defines);
MEM_freeN(vert);
MEM_freeN(frag);
MEM_freeN(defines);
}
return *shader;
}
void workbench_shader_free(void)
{
for (int j = 0; j < sizeof(e_data.opaque_prepass_sh_cache) / sizeof(void *); j++) {
struct GPUShader **sh_array = &e_data.opaque_prepass_sh_cache[0][0][0];
DRW_SHADER_FREE_SAFE(sh_array[j]);
}
for (int j = 0; j < sizeof(e_data.transp_prepass_sh_cache) / sizeof(void *); j++) {
struct GPUShader **sh_array = &e_data.transp_prepass_sh_cache[0][0][0][0];
DRW_SHADER_FREE_SAFE(sh_array[j]);
}
for (int j = 0; j < sizeof(e_data.opaque_composite_sh) / sizeof(void *); j++) {
struct GPUShader **sh_array = &e_data.opaque_composite_sh[0];
DRW_SHADER_FREE_SAFE(sh_array[j]);
}
for (int j = 0; j < sizeof(e_data.shadow_depth_pass_sh) / sizeof(void *); j++) {
struct GPUShader **sh_array = &e_data.shadow_depth_pass_sh[0];
DRW_SHADER_FREE_SAFE(sh_array[j]);
}
for (int j = 0; j < sizeof(e_data.shadow_depth_fail_sh) / sizeof(void *); j++) {
struct GPUShader **sh_array = &e_data.shadow_depth_fail_sh[0][0];
DRW_SHADER_FREE_SAFE(sh_array[j]);
}
for (int j = 0; j < sizeof(e_data.cavity_sh) / sizeof(void *); j++) {
struct GPUShader **sh_array = &e_data.cavity_sh[0][0];
DRW_SHADER_FREE_SAFE(sh_array[j]);
}
for (int j = 0; j < sizeof(e_data.smaa_sh) / sizeof(void *); j++) {
struct GPUShader **sh_array = &e_data.smaa_sh[0];
DRW_SHADER_FREE_SAFE(sh_array[j]);
}
for (int j = 0; j < sizeof(e_data.volume_sh) / sizeof(void *); j++) {
struct GPUShader **sh_array = &e_data.volume_sh[0][0][0];
DRW_SHADER_FREE_SAFE(sh_array[j]);
}
DRW_SHADER_FREE_SAFE(e_data.oit_resolve_sh);
DRW_SHADER_FREE_SAFE(e_data.outline_sh);
DRW_SHADER_FREE_SAFE(e_data.merge_infront_sh);
DRW_SHADER_FREE_SAFE(e_data.dof_prepare_sh);
DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh);
DRW_SHADER_FREE_SAFE(e_data.dof_blur1_sh);
DRW_SHADER_FREE_SAFE(e_data.dof_blur2_sh);
DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh);
DRW_SHADER_FREE_SAFE(e_data.aa_accum_sh);
DRW_SHADER_LIB_FREE_SAFE(e_data.lib);
}

View File

@@ -0,0 +1,367 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2020, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Shadow:
*
* Use stencil shadow buffer to cast a sharp shadow over opaque surfaces.
*
* After the main prepass we render shadow volumes using custom depth & stencil states to
* set the stencil of shadowed area to anything but 0.
*
* Then the shading pass will shade the areas with stencil not equal 0 differently.
*/
#include "DRW_render.h"
#include "BKE_object.h"
#include "BLI_math.h"
#include "workbench_engine.h"
#include "workbench_private.h"
static void compute_parallel_lines_nor_and_dist(const float v1[2],
const float v2[2],
const float v3[2],
float r_line[4])
{
sub_v2_v2v2(r_line, v2, v1);
/* Find orthogonal vector. */
SWAP(float, r_line[0], r_line[1]);
r_line[0] = -r_line[0];
/* Edge distances. */
r_line[2] = dot_v2v2(r_line, v1);
r_line[3] = dot_v2v2(r_line, v3);
/* Make sure r_line[2] is the minimum. */
if (r_line[2] > r_line[3]) {
SWAP(float, r_line[2], r_line[3]);
}
}
static void workbench_shadow_update(WORKBENCH_PrivateData *wpd)
{
wpd->shadow_changed = !compare_v3v3(
wpd->shadow_cached_direction, wpd->shadow_direction_ws, 1e-5f);
if (wpd->shadow_changed) {
float up[3] = {0.0f, 0.0f, 1.0f};
unit_m4(wpd->shadow_mat);
/* TODO fix singularity. */
copy_v3_v3(wpd->shadow_mat[2], wpd->shadow_direction_ws);
cross_v3_v3v3(wpd->shadow_mat[0], wpd->shadow_mat[2], up);
normalize_v3(wpd->shadow_mat[0]);
cross_v3_v3v3(wpd->shadow_mat[1], wpd->shadow_mat[2], wpd->shadow_mat[0]);
invert_m4_m4(wpd->shadow_inv, wpd->shadow_mat);
copy_v3_v3(wpd->shadow_cached_direction, wpd->shadow_direction_ws);
}
float planes[6][4];
DRW_culling_frustum_planes_get(NULL, planes);
/* we only need the far plane. */
copy_v4_v4(wpd->shadow_far_plane, planes[2]);
BoundBox frustum_corners;
DRW_culling_frustum_corners_get(NULL, &frustum_corners);
float shadow_near_corners[4][3];
mul_v3_mat3_m4v3(shadow_near_corners[0], wpd->shadow_inv, frustum_corners.vec[0]);
mul_v3_mat3_m4v3(shadow_near_corners[1], wpd->shadow_inv, frustum_corners.vec[3]);
mul_v3_mat3_m4v3(shadow_near_corners[2], wpd->shadow_inv, frustum_corners.vec[7]);
mul_v3_mat3_m4v3(shadow_near_corners[3], wpd->shadow_inv, frustum_corners.vec[4]);
INIT_MINMAX(wpd->shadow_near_min, wpd->shadow_near_max);
for (int i = 0; i < 4; i++) {
minmax_v3v3_v3(wpd->shadow_near_min, wpd->shadow_near_max, shadow_near_corners[i]);
}
compute_parallel_lines_nor_and_dist(shadow_near_corners[0],
shadow_near_corners[1],
shadow_near_corners[2],
wpd->shadow_near_sides[0]);
compute_parallel_lines_nor_and_dist(shadow_near_corners[1],
shadow_near_corners[2],
shadow_near_corners[0],
wpd->shadow_near_sides[1]);
}
void workbench_shadow_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
float view_matrix[4][4];
DRW_view_viewmat_get(NULL, view_matrix, false);
/* Turn the light in a way where it's more user friendly to control. */
copy_v3_v3(wpd->shadow_direction_ws, scene->display.light_direction);
SWAP(float, wpd->shadow_direction_ws[2], wpd->shadow_direction_ws[1]);
wpd->shadow_direction_ws[2] = -wpd->shadow_direction_ws[2];
wpd->shadow_direction_ws[0] = -wpd->shadow_direction_ws[0];
/* Shadow direction. */
mul_v3_mat3_m4v3(wd->shadow_direction_vs, view_matrix, wpd->shadow_direction_ws);
/* Clamp to avoid overshadowing and shading errors. */
float focus = clamp_f(scene->display.shadow_focus, 0.0001f, 0.99999f);
wd->shadow_shift = scene->display.shadow_shift;
wd->shadow_focus = 1.0f - focus * (1.0f - wd->shadow_shift);
if (SHADOW_ENABLED(wpd)) {
wd->shadow_mul = wpd->shading.shadow_intensity;
wd->shadow_add = 1.0f - wd->shadow_mul;
}
else {
wd->shadow_mul = 0.0f;
wd->shadow_add = 1.0f;
}
}
void workbench_shadow_cache_init(WORKBENCH_Data *data)
{
WORKBENCH_PassList *psl = data->psl;
WORKBENCH_PrivateData *wpd = data->stl->wpd;
struct GPUShader *sh;
DRWShadingGroup *grp;
if (SHADOW_ENABLED(wpd)) {
workbench_shadow_update(wpd);
#if DEBUG_SHADOW_VOLUME
DRWState depth_pass_state = DRW_STATE_DEPTH_LESS;
DRWState depth_fail_state = DRW_STATE_DEPTH_GREATER_EQUAL;
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL;
#else
DRWState depth_pass_state = DRW_STATE_WRITE_STENCIL_SHADOW_PASS;
DRWState depth_fail_state = DRW_STATE_WRITE_STENCIL_SHADOW_FAIL;
DRWState state = DRW_STATE_DEPTH_LESS | DRW_STATE_STENCIL_ALWAYS;
#endif
/* TODO(fclem) Merge into one pass with subpasses. */
DRW_PASS_CREATE(psl->shadow_ps[0], state | depth_pass_state);
DRW_PASS_CREATE(psl->shadow_ps[1], state | depth_fail_state);
/* Stencil Shadow passes. */
for (int manifold = 0; manifold < 2; manifold++) {
sh = workbench_shader_shadow_pass_get(manifold);
wpd->shadow_pass_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[0]);
DRW_shgroup_stencil_mask(grp, 0xFF); /* Needed once to set the stencil state for the pass. */
sh = workbench_shader_shadow_fail_get(manifold, false);
wpd->shadow_fail_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[1]);
DRW_shgroup_stencil_mask(grp, 0xFF); /* Needed once to set the stencil state for the pass. */
sh = workbench_shader_shadow_fail_get(manifold, true);
wpd->shadow_fail_caps_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[1]);
}
}
else {
psl->shadow_ps[0] = NULL;
psl->shadow_ps[1] = NULL;
}
}
static BoundBox *workbench_shadow_object_shadow_bbox_get(WORKBENCH_PrivateData *wpd,
Object *ob,
WORKBENCH_ObjectData *oed)
{
if (oed->shadow_bbox_dirty || wpd->shadow_changed) {
float tmp_mat[4][4];
mul_m4_m4m4(tmp_mat, wpd->shadow_inv, ob->obmat);
/* Get AABB in shadow space. */
INIT_MINMAX(oed->shadow_min, oed->shadow_max);
/* From object space to shadow space */
BoundBox *bbox = BKE_object_boundbox_get(ob);
for (int i = 0; i < 8; i++) {
float corner[3];
mul_v3_m4v3(corner, tmp_mat, bbox->vec[i]);
minmax_v3v3_v3(oed->shadow_min, oed->shadow_max, corner);
}
oed->shadow_depth = oed->shadow_max[2] - oed->shadow_min[2];
/* Extend towards infinity. */
oed->shadow_max[2] += 1e4f;
/* Get extended AABB in world space. */
BKE_boundbox_init_from_minmax(&oed->shadow_bbox, oed->shadow_min, oed->shadow_max);
for (int i = 0; i < 8; i++) {
mul_m4_v3(wpd->shadow_mat, oed->shadow_bbox.vec[i]);
}
oed->shadow_bbox_dirty = false;
}
return &oed->shadow_bbox;
}
static bool workbench_shadow_object_cast_visible_shadow(WORKBENCH_PrivateData *wpd,
Object *ob,
WORKBENCH_ObjectData *oed)
{
BoundBox *shadow_bbox = workbench_shadow_object_shadow_bbox_get(wpd, ob, oed);
const DRWView *default_view = DRW_view_default_get();
return DRW_culling_box_test(default_view, shadow_bbox);
}
static float workbench_shadow_object_shadow_distance(WORKBENCH_PrivateData *wpd,
Object *ob,
WORKBENCH_ObjectData *oed)
{
BoundBox *shadow_bbox = workbench_shadow_object_shadow_bbox_get(wpd, ob, oed);
int corners[4] = {0, 3, 4, 7};
float dist = 1e4f, dist_isect;
for (int i = 0; i < 4; i++) {
if (isect_ray_plane_v3(shadow_bbox->vec[corners[i]],
wpd->shadow_cached_direction,
wpd->shadow_far_plane,
&dist_isect,
true)) {
if (dist_isect < dist) {
dist = dist_isect;
}
}
else {
/* All rays are parallels. If one fails, the other will too. */
break;
}
}
return max_ii(dist - oed->shadow_depth, 0);
}
static bool workbench_shadow_camera_in_object_shadow(WORKBENCH_PrivateData *wpd,
Object *ob,
WORKBENCH_ObjectData *oed)
{
/* Just to be sure the min, max are updated. */
workbench_shadow_object_shadow_bbox_get(wpd, ob, oed);
/* Test if near plane is in front of the shadow. */
if (oed->shadow_min[2] > wpd->shadow_near_max[2]) {
return false;
}
/* Separation Axis Theorem test */
/* Test bbox sides first (faster) */
if ((oed->shadow_min[0] > wpd->shadow_near_max[0]) ||
(oed->shadow_max[0] < wpd->shadow_near_min[0]) ||
(oed->shadow_min[1] > wpd->shadow_near_max[1]) ||
(oed->shadow_max[1] < wpd->shadow_near_min[1])) {
return false;
}
/* Test projected near rectangle sides */
const float pts[4][2] = {
{oed->shadow_min[0], oed->shadow_min[1]},
{oed->shadow_min[0], oed->shadow_max[1]},
{oed->shadow_max[0], oed->shadow_min[1]},
{oed->shadow_max[0], oed->shadow_max[1]},
};
for (int i = 0; i < 2; i++) {
float min_dst = FLT_MAX, max_dst = -FLT_MAX;
for (int j = 0; j < 4; j++) {
float dst = dot_v2v2(wpd->shadow_near_sides[i], pts[j]);
/* Do min max */
if (min_dst > dst) {
min_dst = dst;
}
if (max_dst < dst) {
max_dst = dst;
}
}
if ((wpd->shadow_near_sides[i][2] > max_dst) || (wpd->shadow_near_sides[i][3] < min_dst)) {
return false;
}
}
/* No separation axis found. Both shape intersect. */
return true;
}
static void workbench_init_object_data(DrawData *dd)
{
WORKBENCH_ObjectData *data = (WORKBENCH_ObjectData *)dd;
data->shadow_bbox_dirty = true;
}
void workbench_shadow_cache_populate(WORKBENCH_Data *data, Object *ob, const bool has_transp_mat)
{
WORKBENCH_PrivateData *wpd = data->stl->wpd;
bool is_manifold;
struct GPUBatch *geom_shadow = DRW_cache_object_edge_detection_get(ob, &is_manifold);
if (geom_shadow == NULL) {
return;
}
WORKBENCH_ObjectData *engine_object_data = (WORKBENCH_ObjectData *)DRW_drawdata_ensure(
&ob->id,
&draw_engine_workbench,
sizeof(WORKBENCH_ObjectData),
&workbench_init_object_data,
NULL);
if (workbench_shadow_object_cast_visible_shadow(wpd, ob, engine_object_data)) {
mul_v3_mat3_m4v3(engine_object_data->shadow_dir, ob->imat, wpd->shadow_direction_ws);
DRWShadingGroup *grp;
bool use_shadow_pass_technique = !workbench_shadow_camera_in_object_shadow(
wpd, ob, engine_object_data);
/* Shadow pass technique needs object to be have all its surface opaque. */
if (has_transp_mat) {
use_shadow_pass_technique = false;
}
if (use_shadow_pass_technique) {
grp = DRW_shgroup_create_sub(wpd->shadow_pass_grp[is_manifold]);
DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
DRW_shgroup_uniform_float_copy(grp, "lightDistance", 1e5f);
DRW_shgroup_call_no_cull(grp, geom_shadow, ob);
#if DEBUG_SHADOW_VOLUME
DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){1.0f, 0.0f, 0.0f, 1.0f});
#endif
}
else {
float extrude_distance = workbench_shadow_object_shadow_distance(
wpd, ob, engine_object_data);
/* TODO(fclem): only use caps if they are in the view frustum. */
const bool need_caps = true;
if (need_caps) {
grp = DRW_shgroup_create_sub(wpd->shadow_fail_caps_grp[is_manifold]);
DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance);
DRW_shgroup_call_no_cull(grp, DRW_cache_object_surface_get(ob), ob);
}
grp = DRW_shgroup_create_sub(wpd->shadow_fail_grp[is_manifold]);
DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance);
DRW_shgroup_call_no_cull(grp, geom_shadow, ob);
#if DEBUG_SHADOW_VOLUME
DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){0.0f, 1.0f, 0.0f, 1.0f});
#endif
}
}
}

View File

@@ -1,257 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
#include "BKE_studiolight.h"
#include "workbench_private.h"
#include "BKE_object.h"
#include "BLI_math.h"
void studiolight_update_world(WORKBENCH_PrivateData *wpd,
StudioLight *studiolight,
WORKBENCH_UBO_World *wd)
{
float view_matrix[4][4], rot_matrix[4][4];
DRW_view_viewmat_get(NULL, view_matrix, false);
if (USE_WORLD_ORIENTATION(wpd)) {
axis_angle_to_mat4_single(rot_matrix, 'Z', -wpd->shading.studiolight_rot_z);
mul_m4_m4m4(rot_matrix, view_matrix, rot_matrix);
swap_v3_v3(rot_matrix[2], rot_matrix[1]);
negate_v3(rot_matrix[2]);
}
else {
unit_m4(rot_matrix);
}
if (U.edit_studio_light) {
studiolight = BKE_studiolight_studio_edit_get();
}
/* Studio Lights. */
for (int i = 0; i < 4; i++) {
WORKBENCH_UBO_Light *light = &wd->lights[i];
SolidLight *sl = &studiolight->light[i];
if (sl->flag) {
copy_v3_v3(light->light_direction, sl->vec);
mul_mat3_m4_v3(rot_matrix, light->light_direction);
/* We should predivide the power by PI but that makes the lights really dim. */
copy_v3_v3(light->specular_color, sl->spec);
copy_v3_v3(light->diffuse_color, sl->col);
light->wrapped = sl->smooth;
}
else {
copy_v3_fl3(light->light_direction, 1.0f, 0.0f, 0.0f);
copy_v3_fl(light->specular_color, 0.0f);
copy_v3_fl(light->diffuse_color, 0.0f);
}
}
copy_v3_v3(wd->ambient_color, studiolight->light_ambient);
}
static void compute_parallel_lines_nor_and_dist(const float v1[2],
const float v2[2],
const float v3[2],
float r_line[4])
{
sub_v2_v2v2(r_line, v2, v1);
/* Find orthogonal vector. */
SWAP(float, r_line[0], r_line[1]);
r_line[0] = -r_line[0];
/* Edge distances. */
r_line[2] = dot_v2v2(r_line, v1);
r_line[3] = dot_v2v2(r_line, v3);
/* Make sure r_line[2] is the minimum. */
if (r_line[2] > r_line[3]) {
SWAP(float, r_line[2], r_line[3]);
}
}
void studiolight_update_light(WORKBENCH_PrivateData *wpd, const float light_direction[3])
{
wpd->shadow_changed = !compare_v3v3(wpd->cached_shadow_direction, light_direction, 1e-5f);
if (wpd->shadow_changed) {
float up[3] = {0.0f, 0.0f, 1.0f};
unit_m4(wpd->shadow_mat);
/* TODO fix singularity. */
copy_v3_v3(wpd->shadow_mat[2], light_direction);
cross_v3_v3v3(wpd->shadow_mat[0], wpd->shadow_mat[2], up);
normalize_v3(wpd->shadow_mat[0]);
cross_v3_v3v3(wpd->shadow_mat[1], wpd->shadow_mat[2], wpd->shadow_mat[0]);
invert_m4_m4(wpd->shadow_inv, wpd->shadow_mat);
copy_v3_v3(wpd->cached_shadow_direction, light_direction);
}
float planes[6][4];
DRW_culling_frustum_planes_get(NULL, planes);
/* we only need the far plane. */
copy_v4_v4(wpd->shadow_far_plane, planes[2]);
BoundBox frustum_corners;
DRW_culling_frustum_corners_get(NULL, &frustum_corners);
mul_v3_mat3_m4v3(wpd->shadow_near_corners[0], wpd->shadow_inv, frustum_corners.vec[0]);
mul_v3_mat3_m4v3(wpd->shadow_near_corners[1], wpd->shadow_inv, frustum_corners.vec[3]);
mul_v3_mat3_m4v3(wpd->shadow_near_corners[2], wpd->shadow_inv, frustum_corners.vec[7]);
mul_v3_mat3_m4v3(wpd->shadow_near_corners[3], wpd->shadow_inv, frustum_corners.vec[4]);
INIT_MINMAX(wpd->shadow_near_min, wpd->shadow_near_max);
for (int i = 0; i < 4; i++) {
minmax_v3v3_v3(wpd->shadow_near_min, wpd->shadow_near_max, wpd->shadow_near_corners[i]);
}
compute_parallel_lines_nor_and_dist(wpd->shadow_near_corners[0],
wpd->shadow_near_corners[1],
wpd->shadow_near_corners[2],
wpd->shadow_near_sides[0]);
compute_parallel_lines_nor_and_dist(wpd->shadow_near_corners[1],
wpd->shadow_near_corners[2],
wpd->shadow_near_corners[0],
wpd->shadow_near_sides[1]);
}
static BoundBox *studiolight_object_shadow_bbox_get(WORKBENCH_PrivateData *wpd,
Object *ob,
WORKBENCH_ObjectData *oed)
{
if ((oed->shadow_bbox_dirty) || (wpd->shadow_changed)) {
float tmp_mat[4][4];
mul_m4_m4m4(tmp_mat, wpd->shadow_inv, ob->obmat);
/* Get AABB in shadow space. */
INIT_MINMAX(oed->shadow_min, oed->shadow_max);
/* From object space to shadow space */
BoundBox *bbox = BKE_object_boundbox_get(ob);
for (int i = 0; i < 8; i++) {
float corner[3];
mul_v3_m4v3(corner, tmp_mat, bbox->vec[i]);
minmax_v3v3_v3(oed->shadow_min, oed->shadow_max, corner);
}
oed->shadow_depth = oed->shadow_max[2] - oed->shadow_min[2];
/* Extend towards infinity. */
oed->shadow_max[2] += 1e4f;
/* Get extended AABB in world space. */
BKE_boundbox_init_from_minmax(&oed->shadow_bbox, oed->shadow_min, oed->shadow_max);
for (int i = 0; i < 8; i++) {
mul_m4_v3(wpd->shadow_mat, oed->shadow_bbox.vec[i]);
}
oed->shadow_bbox_dirty = false;
}
return &oed->shadow_bbox;
}
bool studiolight_object_cast_visible_shadow(WORKBENCH_PrivateData *wpd,
Object *ob,
WORKBENCH_ObjectData *oed)
{
BoundBox *shadow_bbox = studiolight_object_shadow_bbox_get(wpd, ob, oed);
const DRWView *default_view = DRW_view_default_get();
return DRW_culling_box_test(default_view, shadow_bbox);
}
float studiolight_object_shadow_distance(WORKBENCH_PrivateData *wpd,
Object *ob,
WORKBENCH_ObjectData *oed)
{
BoundBox *shadow_bbox = studiolight_object_shadow_bbox_get(wpd, ob, oed);
int corners[4] = {0, 3, 4, 7};
float dist = 1e4f, dist_isect;
for (int i = 0; i < 4; i++) {
if (isect_ray_plane_v3(shadow_bbox->vec[corners[i]],
wpd->cached_shadow_direction,
wpd->shadow_far_plane,
&dist_isect,
true)) {
if (dist_isect < dist) {
dist = dist_isect;
}
}
else {
/* All rays are parallels. If one fails, the other will too. */
break;
}
}
return max_ii(dist - oed->shadow_depth, 0);
}
bool studiolight_camera_in_object_shadow(WORKBENCH_PrivateData *wpd,
Object *ob,
WORKBENCH_ObjectData *oed)
{
/* Just to be sure the min, max are updated. */
studiolight_object_shadow_bbox_get(wpd, ob, oed);
/* Test if near plane is in front of the shadow. */
if (oed->shadow_min[2] > wpd->shadow_near_max[2]) {
return false;
}
/* Separation Axis Theorem test */
/* Test bbox sides first (faster) */
if ((oed->shadow_min[0] > wpd->shadow_near_max[0]) ||
(oed->shadow_max[0] < wpd->shadow_near_min[0]) ||
(oed->shadow_min[1] > wpd->shadow_near_max[1]) ||
(oed->shadow_max[1] < wpd->shadow_near_min[1])) {
return false;
}
/* Test projected near rectangle sides */
const float pts[4][2] = {
{oed->shadow_min[0], oed->shadow_min[1]},
{oed->shadow_min[0], oed->shadow_max[1]},
{oed->shadow_max[0], oed->shadow_min[1]},
{oed->shadow_max[0], oed->shadow_max[1]},
};
for (int i = 0; i < 2; i++) {
float min_dst = FLT_MAX, max_dst = -FLT_MAX;
for (int j = 0; j < 4; j++) {
float dst = dot_v2v2(wpd->shadow_near_sides[i], pts[j]);
/* Do min max */
if (min_dst > dst) {
min_dst = dst;
}
if (max_dst < dst) {
max_dst = dst;
}
}
if ((wpd->shadow_near_sides[i][2] > max_dst) || (wpd->shadow_near_sides[i][3] < min_dst)) {
return false;
}
}
/* No separation axis found. Both shape intersect. */
return true;
}

View File

@@ -0,0 +1,180 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2020, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Transparent Pieline:
*
* Use Weight Blended Order Independant Transparency to render transparent surfaces.
*
* The rendering is broken down in two passes:
* - the accumulation pass where we render all the surfaces and accumulate all the weights.
* - the resolve pass where we divide the accumulated infos by the weights.
*
* An additional rerender of the transparent surfaces is sometime done in order to have their
* correct depth and object ids correctly written.
*/
#include "DRW_render.h"
#include "ED_view3d.h"
#include "GPU_extensions.h"
#include "workbench_engine.h"
#include "workbench_private.h"
void workbench_transparent_engine_init(WORKBENCH_Data *data)
{
WORKBENCH_FramebufferList *fbl = data->fbl;
WORKBENCH_PrivateData *wpd = data->stl->wpd;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DrawEngineType *owner = (DrawEngineType *)&workbench_transparent_engine_init;
/* Reuse same format as opaque pipeline to reuse the textures. */
/* Note: Floating point texture is required for the reveal_tex as it is used for
* the alpha accumulation component (see accumulation shader for more explanation). */
const eGPUTextureFormat accum_tex_format = GPU_RGBA16F;
const eGPUTextureFormat reveal_tex_format = NORMAL_ENCODING_ENABLED() ? GPU_RG16F : GPU_RGBA32F;
wpd->accum_buffer_tx = DRW_texture_pool_query_fullscreen(accum_tex_format, owner);
wpd->reveal_buffer_tx = DRW_texture_pool_query_fullscreen(reveal_tex_format, owner);
GPU_framebuffer_ensure_config(&fbl->transp_accum_fb,
{
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
GPU_ATTACHMENT_TEXTURE(wpd->accum_buffer_tx),
GPU_ATTACHMENT_TEXTURE(wpd->reveal_buffer_tx),
});
}
static void workbench_transparent_lighting_uniforms(WORKBENCH_PrivateData *wpd,
DRWShadingGroup *grp)
{
DRW_shgroup_uniform_block_persistent(grp, "world_block", wpd->world_ubo);
DRW_shgroup_uniform_bool_copy(grp, "forceShadowing", false);
if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) {
BKE_studiolight_ensure_flag(wpd->studio_light,
STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE |
STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE);
struct GPUTexture *diff_tx = wpd->studio_light->matcap_diffuse.gputexture;
struct GPUTexture *spec_tx = wpd->studio_light->matcap_specular.gputexture;
const bool use_spec = workbench_is_specular_highlight_enabled(wpd);
spec_tx = (use_spec && spec_tx) ? spec_tx : diff_tx;
DRW_shgroup_uniform_texture_persistent(grp, "matcapDiffuseImage", diff_tx);
DRW_shgroup_uniform_texture_persistent(grp, "matcapSpecularImage", spec_tx);
}
}
void workbench_transparent_cache_init(WORKBENCH_Data *data)
{
WORKBENCH_PassList *psl = data->psl;
WORKBENCH_PrivateData *wpd = data->stl->wpd;
struct GPUShader *sh;
DRWShadingGroup *grp;
{
int transp = 1;
for (int infront = 0; infront < 2; infront++) {
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_OIT;
DRWPass *pass;
if (infront) {
DRW_PASS_CREATE(psl->transp_accum_infront_ps, state | wpd->cull_state | wpd->clip_state);
pass = psl->transp_accum_infront_ps;
}
else {
DRW_PASS_CREATE(psl->transp_accum_ps, state | wpd->cull_state | wpd->clip_state);
pass = psl->transp_accum_ps;
}
for (int hair = 0; hair < 2; hair++) {
wpd->prepass[transp][infront][hair].material_hash = BLI_ghash_ptr_new(__func__);
sh = workbench_shader_transparent_get(wpd, hair);
wpd->prepass[transp][infront][hair].common_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1);
workbench_transparent_lighting_uniforms(wpd, grp);
wpd->prepass[transp][infront][hair].vcol_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */
sh = workbench_shader_transparent_image_get(wpd, hair, false);
wpd->prepass[transp][infront][hair].image_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
workbench_transparent_lighting_uniforms(wpd, grp);
sh = workbench_shader_transparent_image_get(wpd, hair, true);
wpd->prepass[transp][infront][hair].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
workbench_transparent_lighting_uniforms(wpd, grp);
}
}
}
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA;
DRW_PASS_CREATE(psl->transp_resolve_ps, state);
sh = workbench_shader_transparent_resolve_get(wpd);
grp = DRW_shgroup_create(sh, psl->transp_resolve_ps);
DRW_shgroup_uniform_texture(grp, "transparentAccum", wpd->accum_buffer_tx);
DRW_shgroup_uniform_texture(grp, "transparentRevealage", wpd->reveal_buffer_tx);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}
/* Redraw the transparent passes but with depth test
* to output correct outline IDs and depth. */
void workbench_transparent_draw_depth_pass(WORKBENCH_Data *data)
{
WORKBENCH_PrivateData *wpd = data->stl->wpd;
WORKBENCH_FramebufferList *fbl = data->fbl;
WORKBENCH_PassList *psl = data->psl;
const bool do_xray_depth_pass = XRAY_ALPHA(wpd) > 0.0f;
const bool do_transparent_depth_pass = psl->outline_ps || wpd->dof_enabled || do_xray_depth_pass;
if (do_transparent_depth_pass) {
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
if (!DRW_pass_is_empty(psl->transp_accum_ps)) {
GPU_framebuffer_bind(fbl->opaque_fb);
/* TODO(fclem) Disable writting to first two buffers. Unecessary waste of bandwidth. */
DRW_pass_state_set(psl->transp_accum_ps, state | wpd->cull_state | wpd->clip_state);
DRW_draw_pass(psl->transp_accum_ps);
}
if (!DRW_pass_is_empty(psl->transp_accum_infront_ps)) {
GPU_framebuffer_bind(fbl->opaque_infront_fb);
/* TODO(fclem) Disable writting to first two buffers. Unecessary waste of bandwidth. */
DRW_pass_state_set(psl->transp_accum_infront_ps, state | wpd->cull_state | wpd->clip_state);
DRW_draw_pass(psl->transp_accum_infront_ps);
}
}
}

View File

@@ -35,87 +35,23 @@
#include "GPU_draw.h"
enum {
VOLUME_SH_SLICE = 0,
VOLUME_SH_COBA,
VOLUME_SH_CUBIC,
};
#define VOLUME_SH_MAX (1 << (VOLUME_SH_CUBIC + 1))
static struct {
struct GPUShader *volume_sh[VOLUME_SH_MAX];
struct GPUShader *volume_coba_sh;
struct GPUTexture *dummy_tex;
struct GPUTexture *dummy_coba_tex;
} e_data = {{NULL}};
extern char datatoc_workbench_volume_vert_glsl[];
extern char datatoc_workbench_volume_frag_glsl[];
extern char datatoc_common_view_lib_glsl[];
extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
static GPUShader *volume_shader_get(bool slice, bool coba, bool cubic)
void workbench_volume_engine_init(WORKBENCH_Data *vedata)
{
int id = 0;
id += (slice) ? (1 << VOLUME_SH_SLICE) : 0;
id += (coba) ? (1 << VOLUME_SH_COBA) : 0;
id += (cubic) ? (1 << VOLUME_SH_CUBIC) : 0;
WORKBENCH_TextureList *txl = vedata->txl;
if (!e_data.volume_sh[id]) {
DynStr *ds = BLI_dynstr_new();
if (slice) {
BLI_dynstr_append(ds, "#define VOLUME_SLICE\n");
}
if (coba) {
BLI_dynstr_append(ds, "#define USE_COBA\n");
}
if (cubic) {
BLI_dynstr_append(ds, "#define USE_TRICUBIC\n");
}
char *defines = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
char *libs = BLI_string_joinN(datatoc_common_view_lib_glsl,
datatoc_gpu_shader_common_obinfos_lib_glsl);
e_data.volume_sh[id] = DRW_shader_create_with_lib(datatoc_workbench_volume_vert_glsl,
NULL,
datatoc_workbench_volume_frag_glsl,
libs,
defines);
MEM_freeN(libs);
MEM_freeN(defines);
}
return e_data.volume_sh[id];
}
void workbench_volume_engine_init(void)
{
if (!e_data.dummy_tex) {
if (txl->dummy_volume_tx == NULL) {
float pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f};
e_data.dummy_tex = GPU_texture_create_3d(1, 1, 1, GPU_RGBA8, pixel, NULL);
e_data.dummy_coba_tex = GPU_texture_create_1d(1, GPU_RGBA8, pixel, NULL);
txl->dummy_volume_tx = GPU_texture_create_3d(1, 1, 1, GPU_RGBA8, pixel, NULL);
txl->dummy_coba_tx = GPU_texture_create_1d(1, GPU_RGBA8, pixel, NULL);
}
}
void workbench_volume_engine_free(void)
{
for (int i = 0; i < VOLUME_SH_MAX; i++) {
DRW_SHADER_FREE_SAFE(e_data.volume_sh[i]);
}
DRW_TEXTURE_FREE_SAFE(e_data.dummy_tex);
DRW_TEXTURE_FREE_SAFE(e_data.dummy_coba_tex);
}
void workbench_volume_cache_init(WORKBENCH_Data *vedata)
{
vedata->psl->volume_pass = DRW_pass_create(
vedata->psl->volume_ps = DRW_pass_create(
"Volumes", DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_CULL_FRONT);
vedata->stl->wpd->volumes_do = false;
}
void workbench_volume_cache_populate(WORKBENCH_Data *vedata,
@@ -125,8 +61,8 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata,
{
FluidModifierData *mmd = (FluidModifierData *)md;
FluidDomainSettings *mds = mmd->domain;
WORKBENCH_PrivateData *wpd = vedata->stl->g_data;
WORKBENCH_EffectInfo *effect_info = vedata->stl->effects;
WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
WORKBENCH_TextureList *txl = vedata->txl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DRWShadingGroup *grp = NULL;
@@ -154,7 +90,8 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata,
const bool use_slice = (mds->slice_method == FLUID_DOMAIN_SLICE_AXIS_ALIGNED &&
mds->axis_slice_method == AXIS_SLICE_SINGLE);
const bool cubic_interp = (mds->interp_method == VOLUME_INTERP_CUBIC);
GPUShader *sh = volume_shader_get(use_slice, mds->use_coba, cubic_interp);
GPUShader *sh = workbench_shader_volume_get(use_slice, mds->use_coba, cubic_interp);
if (use_slice) {
float invviewmat[4][4];
@@ -168,7 +105,7 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata,
/* 0.05f to achieve somewhat the same opacity as the full view. */
float step_length = max_ff(1e-16f, dim[axis] * 0.05f);
grp = DRW_shgroup_create(sh, vedata->psl->volume_pass);
grp = DRW_shgroup_create(sh, vedata->psl->volume_ps);
DRW_shgroup_uniform_float_copy(grp, "slicePosition", mds->slice_depth);
DRW_shgroup_uniform_int_copy(grp, "sliceAxis", axis);
DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length);
@@ -176,7 +113,7 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata,
}
else {
double noise_ofs;
BLI_halton_1d(3, 0.0, effect_info->jitter_index, &noise_ofs);
BLI_halton_1d(3, 0.0, wpd->taa_sample, &noise_ofs);
float dim[3], step_length, max_slice;
float slice_ct[3] = {mds->res[0], mds->res[1], mds->res[2]};
mul_v3_fl(slice_ct, max_ff(0.001f, mds->slice_per_voxel));
@@ -186,8 +123,8 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata,
mul_v3_v3(dim, slice_ct);
step_length = len_v3(dim);
grp = DRW_shgroup_create(sh, vedata->psl->volume_pass);
DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)wpd->viewvecs, 3);
grp = DRW_shgroup_create(sh, vedata->psl->volume_ps);
DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo);
DRW_shgroup_uniform_int_copy(grp, "samplesLen", max_slice);
DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length);
DRW_shgroup_uniform_float_copy(grp, "noiseOfs", noise_ofs);
@@ -206,9 +143,9 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata,
grp, "densityTexture", (mds->tex_color) ? mds->tex_color : mds->tex_density);
DRW_shgroup_uniform_texture(grp, "shadowTexture", mds->tex_shadow);
DRW_shgroup_uniform_texture(
grp, "flameTexture", (mds->tex_flame) ? mds->tex_flame : e_data.dummy_tex);
grp, "flameTexture", (mds->tex_flame) ? mds->tex_flame : txl->dummy_volume_tx);
DRW_shgroup_uniform_texture(
grp, "flameColorTexture", (mds->tex_flame) ? mds->tex_flame_coba : e_data.dummy_coba_tex);
grp, "flameColorTexture", (mds->tex_flame) ? mds->tex_flame_coba : txl->dummy_coba_tx);
DRW_shgroup_uniform_vec3(
grp, "activeColor", (use_constant_color) ? mds->active_color : white, 1);
}
@@ -225,8 +162,22 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata,
BLI_addtail(&wpd->smoke_domains, BLI_genericNodeN(mmd));
}
void workbench_volume_smoke_textures_free(WORKBENCH_PrivateData *wpd)
void workbench_volume_draw_pass(WORKBENCH_Data *vedata)
{
WORKBENCH_PassList *psl = vedata->psl;
WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
if (wpd->volumes_do) {
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_draw_pass(psl->volume_ps);
}
}
void workbench_volume_draw_finish(WORKBENCH_Data *vedata)
{
WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
/* Free Smoke Textures after rendering */
/* XXX This is a waste of processing and GPU bandwidth if nothing
* is updated. But the problem is since Textures are stored in the
@@ -238,4 +189,4 @@ void workbench_volume_smoke_textures_free(WORKBENCH_PrivateData *wpd)
GPU_free_smoke(mmd);
}
BLI_freelistN(&wpd->smoke_domains);
}
}

View File

@@ -81,6 +81,7 @@ typedef struct DRWPass DRWPass;
typedef struct DRWShadingGroup DRWShadingGroup;
typedef struct DRWUniform DRWUniform;
typedef struct DRWView DRWView;
typedef struct DRWShaderLibrary DRWShaderLibrary;
/* TODO Put it somewhere else? */
typedef struct BoundSphere {
@@ -148,6 +149,8 @@ struct GPUTexture *DRW_texture_pool_query_2d(int w,
int h,
eGPUTextureFormat format,
DrawEngineType *engine_type);
struct GPUTexture *DRW_texture_pool_query_fullscreen(eGPUTextureFormat format,
DrawEngineType *engine_type);
struct GPUTexture *DRW_texture_create_1d(int w,
eGPUTextureFormat format,
@@ -246,6 +249,24 @@ void DRW_shader_free(struct GPUShader *shader);
} \
} while (0)
DRWShaderLibrary *DRW_shader_library_create(void);
/* Warning: Each library must be added after all its dependencies. */
void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name);
#define DRW_SHADER_LIB_ADD(lib, lib_name) \
DRW_shader_library_add_file(lib, datatoc_##lib_name##_glsl, STRINGIFY(lib_name) ".glsl")
char *DRW_shader_library_create_shader_string(DRWShaderLibrary *lib, char *shader_code);
void DRW_shader_library_free(DRWShaderLibrary *lib);
#define DRW_SHADER_LIB_FREE_SAFE(lib) \
do { \
if (lib != NULL) { \
DRW_shader_library_free(lib); \
lib = NULL; \
} \
} while (0)
/* Batches */
typedef enum {
@@ -403,6 +424,9 @@ void DRW_buffer_add_entry_array(DRWCallBuffer *buffer, const void *attr[], uint
DRW_buffer_add_entry_array(buffer, array, (sizeof(array) / sizeof(*array))); \
} while (0)
/* Can only be called during iter phase. */
uint32_t DRW_object_resource_id_get(Object *UNUSED(ob));
void DRW_shgroup_state_enable(DRWShadingGroup *shgroup, DRWState state);
void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state);
@@ -414,7 +438,7 @@ void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state);
void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup,
uint write_mask,
uint reference,
uint comp_mask);
uint compare_mask);
/* TODO remove this function. Obsolete version. mask is actually reference value. */
void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, uint mask);

View File

@@ -177,6 +177,11 @@ struct DRWShadingGroup *DRW_shgroup_hair_create(struct Object *object,
struct DRWPass *hair_pass,
struct GPUShader *shader);
struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md,
struct DRWShadingGroup *shgrp);
struct DRWShadingGroup *DRW_shgroup_material_hair_create(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md,

View File

@@ -108,6 +108,7 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(Object *object,
ParticleSystem *psys,
ModifierData *md,
DRWPass *hair_pass,
DRWShadingGroup *shgrp_parent,
struct GPUMaterial *gpu_mat,
GPUShader *gpu_shader)
{
@@ -127,7 +128,10 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(Object *object,
object, psys, md, &hair_cache, subdiv, thickness_res);
DRWShadingGroup *shgrp;
if (gpu_mat) {
if (shgrp_parent) {
shgrp = DRW_shgroup_create_sub(shgrp_parent);
}
else if (gpu_mat) {
shgrp = DRW_shgroup_material_create(gpu_mat, hair_pass);
}
else if (gpu_shader) {
@@ -151,6 +155,17 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(Object *object,
}
}
/* Fix issue with certain driver not drawing anything if there is no texture bound to
* "ac", "au", "u" or "c". */
if (hair_cache->num_uv_layers == 0) {
DRW_shgroup_uniform_texture(shgrp, "u", hair_cache->final[subdiv].proc_tex);
DRW_shgroup_uniform_texture(shgrp, "au", hair_cache->final[subdiv].proc_tex);
}
if (hair_cache->num_col_layers == 0) {
DRW_shgroup_uniform_texture(shgrp, "c", hair_cache->final[subdiv].proc_tex);
DRW_shgroup_uniform_texture(shgrp, "ac", hair_cache->final[subdiv].proc_tex);
}
if ((dupli_parent != NULL) && (dupli_object != NULL)) {
if (dupli_object->type & OB_DUPLICOLLECTION) {
copy_m4_m4(dupli_mat, dupli_parent->obmat);
@@ -220,7 +235,15 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(Object *object,
DRWShadingGroup *DRW_shgroup_hair_create(
Object *object, ParticleSystem *psys, ModifierData *md, DRWPass *hair_pass, GPUShader *shader)
{
return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, NULL, shader);
return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, NULL, NULL, shader);
}
DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
ParticleSystem *psys,
ModifierData *md,
DRWShadingGroup *shgrp)
{
return drw_shgroup_create_hair_procedural_ex(object, psys, md, NULL, shgrp, NULL, NULL);
}
DRWShadingGroup *DRW_shgroup_material_hair_create(Object *object,
@@ -229,7 +252,7 @@ DRWShadingGroup *DRW_shgroup_material_hair_create(Object *object,
DRWPass *hair_pass,
struct GPUMaterial *material)
{
return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, material, NULL);
return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, NULL, material, NULL);
}
void DRW_hair_update(void)

View File

@@ -1118,45 +1118,23 @@ static void use_drw_engine(DrawEngineType *engine)
BLI_addtail(&DST.enabled_engines, ld);
}
/**
* Use for external render engines.
*/
static void drw_engines_enable_external(void)
{
use_drw_engine(DRW_engine_viewport_external_type.draw_engine);
}
/* TODO revisit this when proper layering is implemented */
/* Gather all draw engines needed and store them in DST.enabled_engines
* That also define the rendering order of engines */
static void drw_engines_enable_from_engine(RenderEngineType *engine_type,
eDrawType drawtype,
bool use_xray)
static void drw_engines_enable_from_engine(RenderEngineType *engine_type, eDrawType drawtype)
{
switch (drawtype) {
case OB_WIRE:
use_drw_engine(&draw_engine_workbench_transparent);
break;
case OB_SOLID:
if (use_xray) {
use_drw_engine(&draw_engine_workbench_transparent);
}
else {
use_drw_engine(&draw_engine_workbench_solid);
}
use_drw_engine(DRW_engine_viewport_workbench_type.draw_engine);
break;
case OB_MATERIAL:
case OB_RENDER:
default:
/* TODO layers */
if (engine_type->draw_engine != NULL) {
use_drw_engine(engine_type->draw_engine);
}
if ((engine_type->flag & RE_INTERNAL) == 0) {
drw_engines_enable_external();
else if ((engine_type->flag & RE_INTERNAL) == 0) {
use_drw_engine(DRW_engine_viewport_external_type.draw_engine);
}
break;
}
@@ -1182,7 +1160,7 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer),
const eDrawType drawtype = v3d->shading.type;
const bool use_xray = XRAY_ENABLED(v3d);
drw_engines_enable_from_engine(engine_type, drawtype, use_xray);
drw_engines_enable_from_engine(engine_type, drawtype);
if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) {
use_drw_engine(&draw_engine_gpencil_type);
}
@@ -1608,6 +1586,9 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph,
GPU_blend(true);
}
GPU_matrix_identity_set();
GPU_matrix_identity_projection_set();
GPU_viewport_unbind_from_offscreen(render_viewport, ofs, do_color_management);
if (draw_background) {
@@ -2646,9 +2627,6 @@ void DRW_engines_register(void)
RE_engines_register(&DRW_engine_viewport_eevee_type);
RE_engines_register(&DRW_engine_viewport_workbench_type);
DRW_engine_register(&draw_engine_workbench_solid);
DRW_engine_register(&draw_engine_workbench_transparent);
DRW_engine_register(&draw_engine_gpencil_type);
DRW_engine_register(&draw_engine_overlay_type);

View File

@@ -562,6 +562,16 @@ static DRWResourceHandle drw_resource_handle_new(float (*obmat)[4], Object *ob)
return handle;
}
uint32_t DRW_object_resource_id_get(Object *UNUSED(ob))
{
DRWResourceHandle handle = DST.ob_handle;
if (handle == 0) {
/* Handle not yet allocated. Return next handle. */
handle = DST.resource_handle;
}
return handle;
}
static DRWResourceHandle drw_resource_handle(DRWShadingGroup *shgroup,
float (*obmat)[4],
Object *ob)
@@ -693,14 +703,14 @@ static void drw_command_set_select_id(DRWShadingGroup *shgroup, GPUVertBuf *buf,
static void drw_command_set_stencil_mask(DRWShadingGroup *shgroup,
uint write_mask,
uint reference,
uint comp_mask)
uint compare_mask)
{
BLI_assert(write_mask <= 0xFF);
BLI_assert(reference <= 0xFF);
BLI_assert(comp_mask <= 0xFF);
BLI_assert(compare_mask <= 0xFF);
DRWCommandSetStencil *cmd = drw_command_create(shgroup, DRW_CMD_STENCIL);
cmd->write_mask = write_mask;
cmd->comp_mask = comp_mask;
cmd->comp_mask = compare_mask;
cmd->ref = reference;
}
@@ -1341,9 +1351,9 @@ void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state)
void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup,
uint write_mask,
uint reference,
uint comp_mask)
uint compare_mask)
{
drw_command_set_stencil_mask(shgroup, write_mask, reference, comp_mask);
drw_command_set_stencil_mask(shgroup, write_mask, reference, compare_mask);
}
/* TODO remove this function. */

View File

@@ -24,6 +24,7 @@
#include "DNA_world_types.h"
#include "DNA_material_types.h"
#include "BLI_dynstr.h"
#include "BLI_listbase.h"
#include "BLI_string_utils.h"
#include "BLI_threads.h"
@@ -282,6 +283,8 @@ void DRW_deferred_shader_remove(GPUMaterial *mat)
/* -------------------------------------------------------------------- */
/** \{ */
GPUShader *DRW_shader_create(const char *vert,
const char *geom,
const char *frag,
@@ -468,3 +471,129 @@ void DRW_shader_free(GPUShader *shader)
{
GPU_shader_free(shader);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shader Library
*
* Simple include system for glsl files.
*
* Usage: Create a DRWShaderLibrary and add the library in the right order.
* You can have nested dependencies but each new library needs to have all its dependencies already
* added to the DRWShaderLibrary.
* Finally you can use DRW_shader_library_create_shader_string to get a shader string that also
* contains the needed libraries for this shader.
* \{ */
/* 32 because we use a 32bit bitmap. */
#define MAX_LIB 32
#define MAX_LIB_NAME 64
#define MAX_LIB_DEPS 8
struct DRWShaderLibrary {
char *libs[MAX_LIB];
char libs_name[MAX_LIB][MAX_LIB_NAME];
uint32_t libs_deps[MAX_LIB];
};
DRWShaderLibrary *DRW_shader_library_create(void)
{
return MEM_callocN(sizeof(DRWShaderLibrary), "DRWShaderLibrary");
}
void DRW_shader_library_free(DRWShaderLibrary *lib)
{
MEM_SAFE_FREE(lib);
}
static int drw_shader_library_search(DRWShaderLibrary *lib, const char *name)
{
for (int i = 0; i < MAX_LIB; i++) {
if (lib->libs[i]) {
if (!strncmp(lib->libs_name[i], name, strlen(lib->libs_name[i]))) {
return i;
}
}
else {
break;
}
}
return -1;
}
/* Return bitmap of dependencies. */
static uint32_t drw_shader_dependencies_get(DRWShaderLibrary *lib, char *lib_code)
{
/* Search dependencies. */
uint32_t deps = 0;
char *haystack = lib_code;
while ((haystack = strstr(haystack, "BLENDER_REQUIRE("))) {
haystack += 16;
int dep = drw_shader_library_search(lib, haystack);
if (dep == -1) {
printf(
"Error: Dependency not found.\n"
"This might be due to bad lib ordering.\n");
BLI_assert(0);
}
else {
deps |= 1u << (uint32_t)dep;
}
}
return deps;
}
void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name)
{
int index = -1;
for (int i = 0; i < MAX_LIB; i++) {
if (lib->libs[i] == NULL) {
index = i;
break;
}
}
if (index > -1) {
lib->libs[index] = lib_code;
BLI_strncpy(lib->libs_name[index], lib_name, MAX_LIB_NAME);
}
else {
printf("Error: Too many libraries. Cannot add %s.\n", lib_name);
BLI_assert(0);
}
lib->libs_deps[index] = drw_shader_dependencies_get(lib, lib_code);
}
/* Return an allocN'ed string containing the shader code with its dependencies prepended.
* Caller must free the string with MEM_freeN after use. */
char *DRW_shader_library_create_shader_string(DRWShaderLibrary *lib, char *shader_code)
{
uint32_t deps = drw_shader_dependencies_get(lib, shader_code);
DynStr *ds = BLI_dynstr_new();
/* Add all dependencies recursively. */
for (int i = MAX_LIB - 1; i > -1; i--) {
if (lib->libs[i] && (deps & (1u << (uint32_t)i))) {
deps |= lib->libs_deps[i];
}
}
/* Concatenate all needed libs into one string. */
for (int i = 0; i < MAX_LIB; i++) {
if (deps & 1u) {
BLI_dynstr_append(ds, lib->libs[i]);
}
deps = deps >> 1;
}
BLI_dynstr_append(ds, shader_code);
char *str = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
return str;
}
/** \} */

View File

@@ -134,6 +134,13 @@ GPUTexture *DRW_texture_pool_query_2d(int w,
return tex;
}
GPUTexture *DRW_texture_pool_query_fullscreen(eGPUTextureFormat format,
DrawEngineType *engine_type)
{
const float *size = DRW_viewport_size_get();
return DRW_texture_pool_query_2d((int)size[0], (int)size[1], format, engine_type);
}
void DRW_texture_ensure_fullscreen_2d(GPUTexture **tex,
eGPUTextureFormat format,
DRWTextureFlag flags)

View File

@@ -1,7 +1,8 @@
/* Need to be included after common_view_lib.glsl for resource_id. */
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#ifndef GPU_OBINFOS_UBO
#define GPU_OBINFOS_UBO
# define GPU_OBINFOS_UBO
struct ObjectInfos {
vec4 drw_OrcoTexCoFactors[2];
vec4 drw_ObjectColor;
@@ -13,7 +14,7 @@ layout(std140) uniform infoBlock
/* DRW_RESOURCE_CHUNK_LEN = 512 */
ObjectInfos drw_infos[512];
};
#define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors)
#define ObjectInfo (drw_infos[resource_id].drw_Infos)
#define ObjectColor (drw_infos[resource_id].drw_ObjectColor)
# define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors)
# define ObjectInfo (drw_infos[resource_id].drw_Infos)
# define ObjectColor (drw_infos[resource_id].drw_ObjectColor)
#endif

View File

@@ -43,10 +43,6 @@ typedef enum eV3DShadingColorType {
V3D_SHADING_TEXTURE_COLOR = 3,
V3D_SHADING_OBJECT_COLOR = 4,
V3D_SHADING_VERTEX_COLOR = 5,
/* Is used to display the object using the error color. For example when in
* solid texture paint mode without any textures configured */
V3D_SHADING_ERROR_COLOR = 999,
} eV3DShadingColorType;
/** #View3DShading.background_type */