diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2018-03-14 00:58:18 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2018-03-14 14:41:00 +0300 |
commit | b4209b138f6bda863009568bf8dc19eb5cf8361c (patch) | |
tree | a41e71adbeaa66c744f393bf3908c6b2ebab9017 /source/blender/draw | |
parent | e22bc559b0a3c918e508f3ac04fb3417fa308947 (diff) |
Clay: Refactor: Port clay to a deferred pipeline.
This means that rendering clay with AO only needs 1 geometry pass.
Thus greatly improving performance of poly heavy scene.
This also fix a self shadow issue in the AO, making low sample count
way better.
We also do not need to blit the depth anymore since we
are doing a fullscreen shading pass.
The constant cost of running the a deferred shading pass is negligeable.
This include quite a bit of code cleanup inside clay_engine.c.
The deferred pipeline is only enabled if at least one material needs it.
Multisampling is not supported yet.
Small hacks when doing deferred:
- We invert the normal before encoding it for precision.
- We put the facing direction into the sign of the mat_id.
- We dither the normal to fight the low bitdepth artifacts of the normal
buffer (which is 8bits per channel to reduce bandwidth usage).
Diffstat (limited to 'source/blender/draw')
-rw-r--r-- | source/blender/draw/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/draw/engines/clay/clay_engine.c | 422 | ||||
-rw-r--r-- | source/blender/draw/engines/clay/shaders/clay_frag.glsl | 73 | ||||
-rw-r--r-- | source/blender/draw/engines/clay/shaders/clay_prepass_frag.glsl | 44 |
4 files changed, 348 insertions, 192 deletions
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 6618aaea4f4..09337ea4aff 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -132,6 +132,7 @@ if(WITH_CLAY_ENGINE) endif() data_to_c_simple(engines/clay/shaders/clay_frag.glsl SRC) +data_to_c_simple(engines/clay/shaders/clay_prepass_frag.glsl SRC) data_to_c_simple(engines/clay/shaders/clay_vert.glsl SRC) data_to_c_simple(engines/clay/shaders/clay_particle_vert.glsl SRC) data_to_c_simple(engines/clay/shaders/clay_particle_strand_frag.glsl SRC) diff --git a/source/blender/draw/engines/clay/clay_engine.c b/source/blender/draw/engines/clay/clay_engine.c index 655031b83b8..9736a9ad027 100644 --- a/source/blender/draw/engines/clay/clay_engine.c +++ b/source/blender/draw/engines/clay/clay_engine.c @@ -51,13 +51,17 @@ #define MAX_CLAY_MAT 512 /* 512 = 9 bit material id */ -#define SHADER_DEFINES \ +#define SHADER_DEFINES_NO_AO \ "#define MAX_MATERIAL " STRINGIFY(MAX_CLAY_MAT) "\n" \ "#define USE_ROTATION\n" \ - "#define USE_AO\n" \ "#define USE_HSV\n" +#define SHADER_DEFINES \ + SHADER_DEFINES_NO_AO \ + "#define USE_AO\n" + extern char datatoc_clay_frag_glsl[]; +extern char datatoc_clay_prepass_frag_glsl[]; extern char datatoc_clay_vert_glsl[]; extern char datatoc_clay_particle_vert_glsl[]; extern char datatoc_clay_particle_strand_frag_glsl[]; @@ -111,6 +115,8 @@ typedef struct CLAY_Storage { int hair_ubo_current_id; DRWShadingGroup *shgrps[MAX_CLAY_MAT]; DRWShadingGroup *shgrps_flat[MAX_CLAY_MAT]; + DRWShadingGroup *shgrps_pre[MAX_CLAY_MAT]; + DRWShadingGroup *shgrps_pre_flat[MAX_CLAY_MAT]; DRWShadingGroup *hair_shgrps[MAX_CLAY_MAT]; } CLAY_Storage; @@ -120,17 +126,20 @@ typedef struct CLAY_StorageList { } CLAY_StorageList; typedef struct CLAY_FramebufferList { - /* default */ - struct GPUFrameBuffer *default_fb; - /* engine specific */ - struct GPUFrameBuffer *dupli_depth; + struct GPUFrameBuffer *dupli_depth_fb; + struct GPUFrameBuffer *prepass_fb; } CLAY_FramebufferList; typedef struct CLAY_PassList { - struct DRWPass *depth_pass; - struct DRWPass *depth_pass_cull; - struct DRWPass *clay_pass; - struct DRWPass *clay_pass_flat; + struct DRWPass *clay_ps; + struct DRWPass *clay_cull_ps; + struct DRWPass *clay_flat_ps; + struct DRWPass *clay_flat_cull_ps; + struct DRWPass *clay_pre_ps; + struct DRWPass *clay_pre_cull_ps; + struct DRWPass *clay_flat_pre_ps; + struct DRWPass *clay_flat_pre_cull_ps; + struct DRWPass *clay_deferred_ps; struct DRWPass *hair_pass; } CLAY_PassList; @@ -154,27 +163,18 @@ typedef struct CLAY_ViewLayerData { /* *********** STATIC *********** */ static struct { - /* Depth Pre Pass */ - struct GPUShader *depth_sh; /* Shading Pass */ struct GPUShader *clay_sh; struct GPUShader *clay_flat_sh; + struct GPUShader *clay_prepass_flat_sh; + struct GPUShader *clay_prepass_sh; + struct GPUShader *clay_deferred_shading_sh; struct GPUShader *hair_sh; - /* Matcap textures */ struct GPUTexture *matcap_array; float matcap_colors[24][4]; - - /* Ssao */ - float winmat[4][4]; - float viewvecs[3][4]; - float ssao_params[4]; - /* Just a serie of int from 0 to MAX_CLAY_MAT-1 */ int ubo_mat_idxs[MAX_CLAY_MAT]; - - /* engine specific */ - struct GPUTexture *depth_dup; } e_data = {NULL}; /* Engine data */ typedef struct CLAY_PrivateData { @@ -184,7 +184,15 @@ typedef struct CLAY_PrivateData { DRWShadingGroup *depth_shgrp_cull; DRWShadingGroup *depth_shgrp_cull_select; DRWShadingGroup *depth_shgrp_cull_active; - bool enable_ao; + /* Deferred shading */ + struct GPUTexture *depth_dup; /* ref only, not alloced */ + struct GPUTexture *normal_tx; /* ref only, not alloced */ + struct GPUTexture *id_tx; /* ref only, not alloced */ + bool enable_deferred_path; + /* Ssao */ + float winmat[4][4]; + float viewvecs[3][4]; + float ssao_params[4]; } CLAY_PrivateData; /* Transient data */ /* Functions */ @@ -366,11 +374,6 @@ static void clay_engine_init(void *vedata) e_data.matcap_array = load_matcaps(prv, 24); } - /* Depth prepass */ - if (!e_data.depth_sh) { - e_data.depth_sh = DRW_shader_create_3D_depth_only(); - } - /* Shading pass */ if (!e_data.clay_sh) { DynStr *ds = BLI_dynstr_new(); @@ -382,13 +385,26 @@ static void clay_engine_init(void *vedata) matcap_with_ao = BLI_dynstr_get_cstring(ds); e_data.clay_sh = DRW_shader_create( - datatoc_clay_vert_glsl, NULL, matcap_with_ao, - SHADER_DEFINES); + datatoc_clay_vert_glsl, NULL, datatoc_clay_frag_glsl, + SHADER_DEFINES_NO_AO); e_data.clay_flat_sh = DRW_shader_create( - datatoc_clay_vert_glsl, NULL, matcap_with_ao, + datatoc_clay_vert_glsl, NULL, datatoc_clay_frag_glsl, + SHADER_DEFINES_NO_AO + "#define USE_FLAT_NORMAL\n"); + + e_data.clay_prepass_sh = DRW_shader_create( + datatoc_clay_vert_glsl, NULL, datatoc_clay_prepass_frag_glsl, + SHADER_DEFINES); + e_data.clay_prepass_flat_sh = DRW_shader_create( + datatoc_clay_vert_glsl, NULL, datatoc_clay_prepass_frag_glsl, SHADER_DEFINES "#define USE_FLAT_NORMAL\n"); + e_data.clay_deferred_shading_sh = DRW_shader_create_fullscreen( + matcap_with_ao, + SHADER_DEFINES + "#define DEFERRED_SHADING\n"); + BLI_dynstr_free(ds); MEM_freeN(matcap_with_ao); } @@ -403,6 +419,12 @@ static void clay_engine_init(void *vedata) stl->storage = MEM_callocN(sizeof(CLAY_Storage), "CLAY_Storage"); } + if (!stl->g_data) { + stl->g_data = MEM_mallocN(sizeof(*stl->g_data), "CLAY_PrivateStorage"); + } + + CLAY_PrivateData *g_data = stl->g_data; + if (!sldata->mat_ubo) { sldata->mat_ubo = DRW_uniformbuffer_create(sizeof(CLAY_UBO_Storage), NULL); } @@ -423,11 +445,20 @@ static void clay_engine_init(void *vedata) } if (DRW_state_is_fbo()) { +#if 0 /* TODO, multisample */ const float *viewport_size = DRW_viewport_size_get(); DRWFboTexture tex = {&e_data.depth_dup, DRW_TEX_DEPTH_24_STENCIL_8, DRW_TEX_TEMP}; - DRW_framebuffer_init(&fbl->dupli_depth, &draw_engine_clay_type, + DRW_framebuffer_init(&fbl->dupli_depth_fb, &draw_engine_clay_type, (int)viewport_size[0], (int)viewport_size[1], &tex, 1); +#endif + + const float *viewport_size = DRW_viewport_size_get(); + DRWFboTexture texs[2] = {{&g_data->normal_tx, DRW_TEX_RG_8, DRW_TEX_TEMP}, + {&g_data->id_tx, DRW_TEX_R_16I, DRW_TEX_TEMP}}; + DRW_framebuffer_init(&fbl->prepass_fb, &draw_engine_clay_type, + (int)viewport_size[0], (int)viewport_size[1], + texs, 2); } /* SSAO setup */ @@ -453,14 +484,14 @@ static void clay_engine_init(void *vedata) DRW_state_dfdy_factors_get(dfdyfacs); - e_data.ssao_params[0] = ssao_samples; - e_data.ssao_params[1] = size[0] / 64.0; - e_data.ssao_params[2] = size[1] / 64.0; - e_data.ssao_params[3] = dfdyfacs[1]; /* dfdy sign for offscreen */ + g_data->ssao_params[0] = ssao_samples; + g_data->ssao_params[1] = size[0] / 64.0; + g_data->ssao_params[2] = size[1] / 64.0; + g_data->ssao_params[3] = dfdyfacs[1]; /* dfdy sign for offscreen */ /* invert the view matrix */ - DRW_viewport_matrix_get(e_data.winmat, DRW_MAT_WIN); - invert_m4_m4(invproj, e_data.winmat); + DRW_viewport_matrix_get(g_data->winmat, DRW_MAT_WIN); + invert_m4_m4(invproj, g_data->winmat); /* convert the view vectors to view space */ for (i = 0; i < 3; i++) { @@ -472,19 +503,19 @@ static void clay_engine_init(void *vedata) mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][2]); viewvecs[i][3] = 1.0; - copy_v4_v4(e_data.viewvecs[i], viewvecs[i]); + copy_v4_v4(g_data->viewvecs[i], viewvecs[i]); } /* we need to store the differences */ - e_data.viewvecs[1][0] -= e_data.viewvecs[0][0]; - e_data.viewvecs[1][1] = e_data.viewvecs[2][1] - e_data.viewvecs[0][1]; + g_data->viewvecs[1][0] -= g_data->viewvecs[0][0]; + g_data->viewvecs[1][1] = g_data->viewvecs[2][1] - g_data->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]); - e_data.viewvecs[1][2] = vec_far[2] - e_data.viewvecs[0][2]; + g_data->viewvecs[1][2] = vec_far[2] - g_data->viewvecs[0][2]; } /* AO Samples Tex */ @@ -503,37 +534,56 @@ static void clay_engine_init(void *vedata) } } -static DRWShadingGroup *CLAY_shgroup_create(CLAY_Data *UNUSED(vedata), DRWPass *pass, int *material_id, bool use_flat) +static DRWShadingGroup *CLAY_shgroup_create(DRWPass *pass, GPUShader *sh, int id) { CLAY_ViewLayerData *sldata = CLAY_view_layer_data_get(); - DRWShadingGroup *grp = DRW_shgroup_create(use_flat ? e_data.clay_flat_sh : e_data.clay_sh, pass); - - DRW_shgroup_uniform_vec2(grp, "screenres", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_buffer(grp, "depthtex", &e_data.depth_dup); + DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); DRW_shgroup_uniform_texture(grp, "matcaps", e_data.matcap_array); - DRW_shgroup_uniform_mat4(grp, "WinMatrix", (float *)e_data.winmat); - DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)e_data.viewvecs, 3); - DRW_shgroup_uniform_vec4(grp, "ssao_params", e_data.ssao_params, 1); + DRW_shgroup_uniform_block(grp, "material_block", sldata->mat_ubo); + DRW_shgroup_uniform_block(grp, "matcaps_block", sldata->matcaps_ubo); + DRW_shgroup_uniform_int(grp, "mat_id", &e_data.ubo_mat_idxs[id], 1); + return grp; +} + +static DRWShadingGroup *CLAY_shgroup_deferred_prepass_create(DRWPass *pass, GPUShader *sh, int id) +{ + DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_stencil_mask(grp, 1); + DRW_shgroup_uniform_int(grp, "mat_id", &e_data.ubo_mat_idxs[id], 1); - DRW_shgroup_uniform_int(grp, "mat_id", material_id, 1); + return grp; +} +static DRWShadingGroup *CLAY_shgroup_deferred_shading_create(DRWPass *pass, CLAY_PrivateData *g_data) +{ + CLAY_ViewLayerData *sldata = CLAY_view_layer_data_get(); + DRWShadingGroup *grp = DRW_shgroup_create(e_data.clay_deferred_shading_sh, pass); + DRW_shgroup_stencil_mask(grp, 1); + DRW_shgroup_uniform_buffer(grp, "depthtex", &g_data->depth_dup); + DRW_shgroup_uniform_buffer(grp, "normaltex", &g_data->normal_tx); + DRW_shgroup_uniform_buffer(grp, "idtex", &g_data->id_tx); + DRW_shgroup_uniform_texture(grp, "matcaps", e_data.matcap_array); DRW_shgroup_uniform_texture(grp, "ssao_jitter", sldata->jitter_tx); DRW_shgroup_uniform_block(grp, "samples_block", sldata->sampling_ubo); DRW_shgroup_uniform_block(grp, "material_block", sldata->mat_ubo); DRW_shgroup_uniform_block(grp, "matcaps_block", sldata->matcaps_ubo); - + /* TODO put in ubo */ + DRW_shgroup_uniform_mat4(grp, "WinMatrix", (float *)g_data->winmat); + DRW_shgroup_uniform_vec2(grp, "invscreenres", DRW_viewport_invert_size_get(), 1); + DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)g_data->viewvecs, 3); + DRW_shgroup_uniform_vec4(grp, "ssao_params", g_data->ssao_params, 1); return grp; } -static DRWShadingGroup *CLAY_hair_shgroup_create(CLAY_Data *UNUSED(vedata), DRWPass *pass, int *material_id) +static DRWShadingGroup *CLAY_hair_shgroup_create(DRWPass *pass, int id) { CLAY_ViewLayerData *sldata = CLAY_view_layer_data_get(); - DRWShadingGroup *grp = DRW_shgroup_create(e_data.hair_sh, pass); + DRWShadingGroup *grp = DRW_shgroup_create(e_data.hair_sh, pass); DRW_shgroup_uniform_texture(grp, "matcaps", e_data.matcap_array); - DRW_shgroup_uniform_int(grp, "mat_id", material_id, 1); DRW_shgroup_uniform_block(grp, "material_block", sldata->mat_ubo); DRW_shgroup_uniform_block(grp, "matcaps_block", sldata->matcaps_ubo); + DRW_shgroup_uniform_int(grp, "mat_id", &e_data.ubo_mat_idxs[id], 1); return grp; } @@ -568,25 +618,17 @@ static int search_hair_mat_to_ubo(CLAY_Storage *storage, const CLAY_HAIR_UBO_Mat static int push_mat_to_ubo(CLAY_Storage *storage, const CLAY_UBO_Material *mat_ubo_test) { - int id = storage->ubo_current_id; - CLAY_UBO_Material *ubo = &storage->mat_storage.materials[id]; - - *ubo = *mat_ubo_test; - - storage->ubo_current_id++; - + int id = storage->ubo_current_id++; + id = min_ii(MAX_CLAY_MAT, id); + storage->mat_storage.materials[id] = *mat_ubo_test; return id; } static int push_hair_mat_to_ubo(CLAY_Storage *storage, const CLAY_HAIR_UBO_Material *hair_mat_ubo_test) { - int id = storage->hair_ubo_current_id; - CLAY_HAIR_UBO_Material *ubo = &storage->hair_mat_storage.materials[id]; - - *ubo = *hair_mat_ubo_test; - - storage->hair_ubo_current_id++; - + int id = storage->hair_ubo_current_id++; + id = min_ii(MAX_CLAY_MAT, id); + storage->hair_mat_storage.materials[id] = *hair_mat_ubo_test; return id; } @@ -616,11 +658,11 @@ static int hair_mat_in_ubo(CLAY_Storage *storage, const CLAY_HAIR_UBO_Material * return id; } -static void ubo_mat_from_object(Object *ob, CLAY_UBO_Material *r_ubo, bool *r_needs_ao) +static void ubo_mat_from_object(CLAY_Storage *storage, Object *ob, bool *r_needs_ao, int *r_id) { IDProperty *props = BKE_layer_collection_engine_evaluated_get(ob, COLLECTION_MODE_NONE, RE_engine_id_BLENDER_CLAY); - /* Default Settings */ + int matcap_icon = BKE_collection_engine_property_value_get_int(props, "matcap_icon"); float matcap_rot = BKE_collection_engine_property_value_get_float(props, "matcap_rotation"); float matcap_hue = BKE_collection_engine_property_value_get_float(props, "matcap_hue"); float matcap_sat = BKE_collection_engine_property_value_get_float(props, "matcap_saturation"); @@ -629,41 +671,45 @@ static void ubo_mat_from_object(Object *ob, CLAY_UBO_Material *r_ubo, bool *r_ne float ssao_factor_cavity = BKE_collection_engine_property_value_get_float(props, "ssao_factor_cavity"); float ssao_factor_edge = BKE_collection_engine_property_value_get_float(props, "ssao_factor_edge"); float ssao_attenuation = BKE_collection_engine_property_value_get_float(props, "ssao_attenuation"); - int matcap_icon = BKE_collection_engine_property_value_get_int(props, "matcap_icon"); + + CLAY_UBO_Material r_ubo = {{0.0f}}; if (((ssao_factor_cavity > 0.0) || (ssao_factor_edge > 0.0)) && (ssao_distance > 0.0)) { *r_needs_ao = true; + + r_ubo.ssao_params_var[0] = ssao_distance; + r_ubo.ssao_params_var[1] = ssao_factor_cavity; + r_ubo.ssao_params_var[2] = ssao_factor_edge; + r_ubo.ssao_params_var[3] = ssao_attenuation; + } + else { + *r_needs_ao = false; } - memset(r_ubo, 0x0, sizeof(*r_ubo)); + r_ubo.matcap_rot[0] = cosf(matcap_rot * 3.14159f * 2.0f); + r_ubo.matcap_rot[1] = sinf(matcap_rot * 3.14159f * 2.0f); - r_ubo->matcap_rot[0] = cosf(matcap_rot * 3.14159f * 2.0f); - r_ubo->matcap_rot[1] = sinf(matcap_rot * 3.14159f * 2.0f); + r_ubo.matcap_hsv[0] = matcap_hue + 0.5f; + r_ubo.matcap_hsv[1] = matcap_sat * 2.0f; + r_ubo.matcap_hsv[2] = matcap_val * 2.0f; - r_ubo->matcap_hsv[0] = matcap_hue + 0.5f; - r_ubo->matcap_hsv[1] = matcap_sat * 2.0f; - r_ubo->matcap_hsv[2] = matcap_val * 2.0f; + r_ubo.matcap_id = matcap_to_index(matcap_icon); - r_ubo->ssao_params_var[0] = ssao_distance; - r_ubo->ssao_params_var[1] = ssao_factor_cavity; - r_ubo->ssao_params_var[2] = ssao_factor_edge; - r_ubo->ssao_params_var[3] = ssao_attenuation; - r_ubo->matcap_id = matcap_to_index(matcap_icon); + *r_id = mat_in_ubo(storage, &r_ubo); } -static void hair_ubo_mat_from_object(Object *ob, CLAY_HAIR_UBO_Material *r_ubo) +static void hair_ubo_mat_from_object(Object *ob, CLAY_HAIR_UBO_Material *r_ubo) { IDProperty *props = BKE_layer_collection_engine_evaluated_get(ob, COLLECTION_MODE_NONE, RE_engine_id_BLENDER_CLAY); - /* Default Settings */ + int matcap_icon = BKE_collection_engine_property_value_get_int(props, "matcap_icon"); float matcap_rot = BKE_collection_engine_property_value_get_float(props, "matcap_rotation"); float matcap_hue = BKE_collection_engine_property_value_get_float(props, "matcap_hue"); float matcap_sat = BKE_collection_engine_property_value_get_float(props, "matcap_saturation"); float matcap_val = BKE_collection_engine_property_value_get_float(props, "matcap_value"); float hair_randomness = BKE_collection_engine_property_value_get_float(props, "hair_brightness_randomness"); - int matcap_icon = BKE_collection_engine_property_value_get_int(props, "matcap_icon"); memset(r_ubo, 0x0, sizeof(*r_ubo)); @@ -676,25 +722,55 @@ static void hair_ubo_mat_from_object(Object *ob, CLAY_HAIR_UBO_Material *r_ubo) r_ubo->matcap_id = matcap_to_index(matcap_icon); } -static DRWShadingGroup *CLAY_object_shgrp_get( - CLAY_Data *vedata, Object *ob, CLAY_StorageList *stl, CLAY_PassList *psl, bool use_flat) +static DRWShadingGroup *CLAY_object_shgrp_get(CLAY_Data *vedata, Object *ob, bool use_flat, bool cull) { - DRWShadingGroup **shgrps = use_flat ? stl->storage->shgrps_flat : stl->storage->shgrps; - CLAY_UBO_Material mat_ubo_test; + bool prepass; int id; + CLAY_PassList *psl = vedata->psl; + CLAY_Storage *storage = vedata->stl->storage; + DRWShadingGroup **shgrps; + DRWPass *pass; GPUShader *sh; + + ubo_mat_from_object(storage, ob, &prepass, &id); + + if (prepass) { + if (use_flat) { + shgrps = storage->shgrps_pre_flat; + pass = (cull) ? psl->clay_flat_pre_cull_ps : psl->clay_flat_pre_ps; + sh = e_data.clay_prepass_flat_sh; + } + else { + shgrps = storage->shgrps_pre; + pass = (cull) ? psl->clay_pre_cull_ps : psl->clay_pre_ps; + sh = e_data.clay_prepass_sh; + } - ubo_mat_from_object(ob, &mat_ubo_test, &stl->g_data->enable_ao); + if (shgrps[id] == NULL) { + shgrps[id] = CLAY_shgroup_deferred_prepass_create(pass, sh, id); + } - int id = mat_in_ubo(stl->storage, &mat_ubo_test); + vedata->stl->g_data->enable_deferred_path = true; + } + else { + if (use_flat) { + shgrps = storage->shgrps_flat; + pass = (cull) ? psl->clay_flat_cull_ps : psl->clay_flat_ps; + sh = e_data.clay_flat_sh; + } + else { + shgrps = storage->shgrps; + pass = (cull) ? psl->clay_cull_ps : psl->clay_ps; + sh = e_data.clay_sh; + } - if (shgrps[id] == NULL) { - shgrps[id] = CLAY_shgroup_create( - vedata, use_flat ? psl->clay_pass_flat : psl->clay_pass, &e_data.ubo_mat_idxs[id], use_flat); + if (shgrps[id] == NULL) { + shgrps[id] = CLAY_shgroup_create(pass, sh, id); + } } return shgrps[id]; } -static DRWShadingGroup *CLAY_hair_shgrp_get(CLAY_Data *vedata, Object *ob, CLAY_StorageList *stl, CLAY_PassList *psl) +static DRWShadingGroup *CLAY_hair_shgrp_get(CLAY_Data *UNUSED(vedata), Object *ob, CLAY_StorageList *stl, CLAY_PassList *psl) { DRWShadingGroup **hair_shgrps = stl->storage->hair_shgrps; @@ -704,54 +780,45 @@ static DRWShadingGroup *CLAY_hair_shgrp_get(CLAY_Data *vedata, Object *ob, CLAY_ int hair_id = hair_mat_in_ubo(stl->storage, &hair_mat_ubo_test); if (hair_shgrps[hair_id] == NULL) { - hair_shgrps[hair_id] = CLAY_hair_shgroup_create(vedata, psl->hair_pass, &e_data.ubo_mat_idxs[hair_id]); + hair_shgrps[hair_id] = CLAY_hair_shgroup_create(psl->hair_pass, hair_id); } return hair_shgrps[hair_id]; } -static DRWShadingGroup *CLAY_object_shgrp_default_mode_get( - CLAY_Data *vedata, Object *ob, CLAY_StorageList *stl, CLAY_PassList *psl) -{ - bool use_flat = DRW_object_is_flat_normal(ob); - return CLAY_object_shgrp_get(vedata, ob, stl, psl, use_flat); -} - static void clay_cache_init(void *vedata) { CLAY_PassList *psl = ((CLAY_Data *)vedata)->psl; CLAY_StorageList *stl = ((CLAY_Data *)vedata)->stl; - - if (!stl->g_data) { - /* Alloc transient pointers */ - stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__); - } + const bool multisample = false; /* Disable AO unless a material needs it. */ - stl->g_data->enable_ao = false; + stl->g_data->enable_deferred_path = false; - /* Depth Pass */ - { - psl->depth_pass = DRW_pass_create("Depth Pass", DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); - stl->g_data->depth_shgrp = DRW_shgroup_create(e_data.depth_sh, psl->depth_pass); + /* Reset UBO datas, shgrp pointers and material id counters. */ + memset(stl->storage, 0, sizeof(*stl->storage)); - psl->depth_pass_cull = DRW_pass_create( - "Depth Pass Cull", - DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_CULL_BACK); - stl->g_data->depth_shgrp_cull = DRW_shgroup_create(e_data.depth_sh, psl->depth_pass_cull); - } - - /* Clay Pass */ + /* Solid Passes */ { - psl->clay_pass = DRW_pass_create("Clay Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - stl->storage->ubo_current_id = 0; - memset(stl->storage->shgrps, 0, sizeof(DRWShadingGroup *) * MAX_CLAY_MAT); - } - - /* Clay Pass (Flat) */ - { - psl->clay_pass_flat = DRW_pass_create("Clay Pass Flat", DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - memset(stl->storage->shgrps_flat, 0, sizeof(DRWShadingGroup *) * MAX_CLAY_MAT); + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + psl->clay_ps = DRW_pass_create("Clay", state); + psl->clay_cull_ps = DRW_pass_create("Clay Culled", state | DRW_STATE_CULL_BACK); + psl->clay_flat_ps = DRW_pass_create("Clay Flat", state); + psl->clay_flat_cull_ps = DRW_pass_create("Clay Flat Culled", state | DRW_STATE_CULL_BACK); + + DRWState prepass_state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | + ((multisample) ? DRW_STATE_WRITE_STENCIL : 0); + DRWState prepass_cull_state = prepass_state | DRW_STATE_CULL_BACK; + psl->clay_pre_ps = DRW_pass_create("Clay Deferred Pre", prepass_state); + psl->clay_pre_cull_ps = DRW_pass_create("Clay Deferred Pre Culled", prepass_cull_state); + psl->clay_flat_pre_ps = DRW_pass_create("Clay Deferred Flat Pre", prepass_state); + psl->clay_flat_pre_cull_ps = DRW_pass_create("Clay Deferred Flat Pre Culled", prepass_cull_state); + + DRWState deferred_state = DRW_STATE_WRITE_COLOR | ((multisample) ? DRW_STATE_STENCIL_EQUAL : 0); + psl->clay_deferred_ps = DRW_pass_create("Clay Deferred Shading", deferred_state); + + DRWShadingGroup *grp = CLAY_shgroup_deferred_shading_create(psl->clay_deferred_ps, stl->g_data); + DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL); } /* Hair Pass */ @@ -759,8 +826,6 @@ static void clay_cache_init(void *vedata) psl->hair_pass = DRW_pass_create( "Hair Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_WIRE); - stl->storage->hair_ubo_current_id = 0; - memset(stl->storage->hair_shgrps, 0, sizeof(DRWShadingGroup *) * MAX_CLAY_MAT); } } @@ -795,9 +860,6 @@ static void clay_cache_populate_particles(void *vedata, Object *ob) static void clay_cache_populate(void *vedata, Object *ob) { - CLAY_PassList *psl = ((CLAY_Data *)vedata)->psl; - CLAY_StorageList *stl = ((CLAY_Data *)vedata)->stl; - DRWShadingGroup *clay_shgrp; if (!DRW_object_is_renderable(ob)) @@ -825,26 +887,9 @@ static void clay_cache_populate(void *vedata, Object *ob) IDProperty *ces_mode_ob = BKE_layer_collection_engine_evaluated_get(ob, COLLECTION_MODE_OBJECT, ""); const bool do_cull = BKE_collection_engine_property_value_get_bool(ces_mode_ob, "show_backface_culling"); const bool is_sculpt_mode = is_active && (draw_ctx->object_mode & OB_MODE_SCULPT) != 0; - const bool is_default_mode_shader = is_sculpt_mode; - - /* Depth Prepass */ - { - DRWShadingGroup *depth_shgrp = do_cull ? stl->g_data->depth_shgrp_cull : stl->g_data->depth_shgrp; - if (is_sculpt_mode) { - DRW_shgroup_call_sculpt_add(depth_shgrp, ob, ob->obmat); - } - else { - DRW_shgroup_call_object_add(depth_shgrp, geom, ob); - } - } + const bool use_flat = is_sculpt_mode && DRW_object_is_flat_normal(ob); - /* Shading */ - if (is_default_mode_shader) { - clay_shgrp = CLAY_object_shgrp_default_mode_get(vedata, ob, stl, psl); - } - else { - clay_shgrp = CLAY_object_shgrp_get(vedata, ob, stl, psl, false); - } + clay_shgrp = CLAY_object_shgrp_get(vedata, ob, use_flat, do_cull); if (is_sculpt_mode) { DRW_shgroup_call_sculpt_add(clay_shgrp, ob, ob->obmat); @@ -870,37 +915,53 @@ static void clay_draw_scene(void *vedata) CLAY_PassList *psl = ((CLAY_Data *)vedata)->psl; CLAY_FramebufferList *fbl = ((CLAY_Data *)vedata)->fbl; DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + const bool multisample = false; + + /* Passes are ordered to have less _potential_ overdraw */ + DRW_draw_pass(psl->clay_cull_ps); + DRW_draw_pass(psl->clay_flat_cull_ps); + DRW_draw_pass(psl->clay_ps); + DRW_draw_pass(psl->clay_flat_ps); + DRW_draw_pass(psl->hair_pass); - /* Pass 1 : Depth pre-pass */ - if (stl->g_data->enable_ao) { - DRW_draw_pass(psl->depth_pass); - DRW_draw_pass(psl->depth_pass_cull); - } - else { - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; - DRW_pass_state_set(psl->clay_pass, state); - DRW_pass_state_set(psl->clay_pass_flat, state); - } + if (stl->g_data->enable_deferred_path) { + if (DRW_state_is_fbo()) { + DRW_framebuffer_texture_detach(dtxl->depth); + DRW_framebuffer_texture_attach(fbl->prepass_fb, dtxl->depth, 0, 0); + DRW_framebuffer_texture_attach(fbl->prepass_fb, stl->g_data->normal_tx, 0, 0); + DRW_framebuffer_texture_attach(fbl->prepass_fb, stl->g_data->id_tx, 1, 0); + DRW_framebuffer_bind(fbl->prepass_fb); + /* We need to clear the id texture unfortunately. */ + DRW_framebuffer_clear(true, false, false, (float[4]){0.0f, 0.0f, 0.0f, 0.0f}, 0.0f); + } + + DRW_draw_pass(psl->clay_pre_cull_ps); + DRW_draw_pass(psl->clay_flat_pre_cull_ps); + DRW_draw_pass(psl->clay_pre_ps); + DRW_draw_pass(psl->clay_flat_pre_ps); - /* Pass 2 : Duplicate depth */ - /* Unless we go for deferred shading we need this to avoid manual depth test and artifacts */ - if (DRW_state_is_fbo() && stl->g_data->enable_ao) { - /* attach temp textures */ - DRW_framebuffer_texture_attach(fbl->dupli_depth, e_data.depth_dup, 0, 0); + if (DRW_state_is_fbo()) { + DRW_framebuffer_texture_detach(dtxl->depth); - DRW_framebuffer_blit(dfbl->default_fb, fbl->dupli_depth, true, false); + if (multisample) { + /* For multisample, we need to copy the depth tex (all samples). */ + DRW_framebuffer_texture_attach(fbl->dupli_depth_fb, stl->g_data->depth_dup, 0, 0); + DRW_framebuffer_blit(dfbl->default_fb, fbl->dupli_depth_fb, true, false); + DRW_framebuffer_texture_detach(stl->g_data->depth_dup); + } + else { + stl->g_data->depth_dup = dtxl->depth; + } - /* detach temp textures */ - DRW_framebuffer_texture_detach(e_data.depth_dup); + DRW_framebuffer_bind(dfbl->default_fb); - /* restore default fb */ - DRW_framebuffer_bind(dfbl->default_fb); - } + DRW_draw_pass(psl->clay_deferred_ps); - /* Pass 3 : Shading */ - DRW_draw_pass(psl->clay_pass); - DRW_draw_pass(psl->clay_pass_flat); - DRW_draw_pass(psl->hair_pass); + DRW_framebuffer_texture_attach(dfbl->default_fb, dtxl->depth, 0, 0); + DRW_framebuffer_bind(dfbl->default_fb); + } + } } static void clay_layer_collection_settings_create(RenderEngine *UNUSED(engine), IDProperty *props) @@ -935,6 +996,9 @@ static void clay_engine_free(void) { DRW_SHADER_FREE_SAFE(e_data.clay_sh); DRW_SHADER_FREE_SAFE(e_data.clay_flat_sh); + DRW_SHADER_FREE_SAFE(e_data.clay_prepass_flat_sh); + DRW_SHADER_FREE_SAFE(e_data.clay_prepass_sh); + DRW_SHADER_FREE_SAFE(e_data.clay_deferred_shading_sh); DRW_SHADER_FREE_SAFE(e_data.hair_sh); DRW_TEXTURE_FREE_SAFE(e_data.matcap_array); } diff --git a/source/blender/draw/engines/clay/shaders/clay_frag.glsl b/source/blender/draw/engines/clay/shaders/clay_frag.glsl index 619843e2a02..f2c6cd5f780 100644 --- a/source/blender/draw/engines/clay/shaders/clay_frag.glsl +++ b/source/blender/draw/engines/clay/shaders/clay_frag.glsl @@ -1,5 +1,4 @@ -uniform vec2 screenres; -uniform sampler2D depthtex; +uniform vec2 invscreenres; uniform mat4 WinMatrix; /* Matcap */ @@ -33,7 +32,14 @@ layout(std140) uniform material_block { Material matcaps_param[MAX_MATERIAL]; }; +#ifdef DEFERRED_SHADING +uniform sampler2D depthtex; +uniform sampler2D normaltex; +uniform isampler2D idtex; +int mat_id; /* global */ +#else uniform int mat_id; +#endif /* Aliases */ #define ssao_samples_num ssao_params.x @@ -44,10 +50,12 @@ uniform int mat_id; #define matcap_index matcaps_param[mat_id].matcap_hsv_id.w #define matcap_rotation matcaps_param[mat_id].matcap_rot.xy -#ifdef USE_FLAT_NORMAL +#ifndef DEFERRED_SHADING +# ifdef USE_FLAT_NORMAL flat in vec3 normal; -#else +# else in vec3 normal; +# endif #endif out vec4 fragColor; @@ -169,24 +177,33 @@ void ssao_factors( out float cavities, out float edges); #endif -void main() { - vec2 screenco = vec2(gl_FragCoord.xy) / screenres; - float depth = texture(depthtex, screenco).r; - - vec3 position = get_view_space_from_depth(screenco, depth); +/* From http://aras-p.info/texts/CompactNormalStorage.html + * Using Method #4: Spheremap Transform */ +vec3 normal_decode(vec2 enc) +{ + vec2 fenc = enc * 4.0 - 2.0; + float f = dot(fenc, fenc); + float g = sqrt(1.0 - f / 4.0); + vec3 n; + n.xy = fenc*g; + n.z = 1 - f / 2; + return n; +} +vec3 shade(vec3 N, vec3 position, float depth, vec2 screenco) +{ #ifdef USE_ROTATION /* Rotate texture coordinates */ vec2 rotY = vec2(-matcap_rotation.y, matcap_rotation.x); - vec2 texco = abs(vec2(dot(normal.xy, matcap_rotation), dot(normal.xy, rotY)) * .49 + 0.5); + vec2 texco = abs(vec2(dot(N.xy, matcap_rotation), dot(N.xy, rotY)) * .49 + 0.5); #else - vec2 texco = abs(normal.xy * .49 + 0.5); + vec2 texco = abs(N.xy * .49 + 0.5); #endif vec3 col = texture(matcaps, vec3(texco, matcap_index)).rgb; #ifdef USE_AO - float cavity, edges; - ssao_factors(depth, normal, position, screenco, cavity, edges); + float cavity = 0.0, edges = 0.0; + ssao_factors(depth, N, position, screenco, cavity, edges); col *= mix(vec3(1.0), matcaps_color[int(matcap_index)].rgb, cavity); #endif @@ -200,5 +217,35 @@ void main() { col *= edges + 1.0; #endif + return col; +} + +void main() +{ + vec2 screenco = vec2(gl_FragCoord.xy) * invscreenres; + +#ifdef DEFERRED_SHADING + mat_id = texture(idtex, screenco).r; + + /* early out (manual stencil test) */ + if (mat_id == 0) + discard; + + float depth = texture(depthtex, screenco).r; + vec3 N = normal_decode(texture(normaltex, screenco).rg); + /* see the prepass for explanations. */ + if (mat_id < 0) { + N = -N; + } + mat_id = abs(mat_id) - 1; +#else + float depth = gl_FragCoord.z; + vec3 N = normal; +#endif + + vec3 position = get_view_space_from_depth(screenco, depth); + + vec3 col = shade(N, position, depth, screenco); + fragColor = vec4(col, 1.0); } diff --git a/source/blender/draw/engines/clay/shaders/clay_prepass_frag.glsl b/source/blender/draw/engines/clay/shaders/clay_prepass_frag.glsl new file mode 100644 index 00000000000..8eb22a7854c --- /dev/null +++ b/source/blender/draw/engines/clay/shaders/clay_prepass_frag.glsl @@ -0,0 +1,44 @@ +uniform int mat_id; + +#ifdef USE_FLAT_NORMAL +flat in vec3 normal; +#else +in vec3 normal; +#endif + +layout(location = 0) out vec2 outNormals; +layout(location = 1) out int outIndex; + +/* From http://aras-p.info/texts/CompactNormalStorage.html + * Using Method #4: Spheremap Transform */ +vec2 normal_encode(vec3 n) +{ + float p = sqrt(n.z * 8.0 + 8.0); + return n.xy / p + 0.5; +} + +/* 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 mat4 dither_mat = mat4( + 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)) +); + +void main() { + outIndex = (mat_id + 1); /* 0 is clear color */ + /** + * To fix the normal buffer precision issue for backfaces, + * we invert normals and use the sign of the index buffer + * to tag them, and re-invert in deferred pass. + **/ + vec3 N = (gl_FrontFacing) ? normal : -normal; + outIndex = (gl_FrontFacing) ? outIndex : -outIndex; + + outNormals = normal_encode(N); + + /* Dither the output to fight low quality. */ + ivec2 tx = ivec2(gl_FragCoord.xy) % 4; + outNormals += dither_mat[tx.x][tx.y]; +} |