diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-06-04 13:12:58 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-06-04 13:12:58 +0300 |
commit | 28b597b6df46b6d2581d7948578922bb3566d242 (patch) | |
tree | 5b79613f8387d44ce66dc415b629b55906cabf93 /source/blender/draw/engines/eevee/eevee_materials.c | |
parent | 8d1e6d7833177023a6af0d040dabf4d5690b87db (diff) |
Eevee: Material code refactor.
Separate material handling inside another file.
Make use of enums to identify shader variations.
Group all 64*64 LUTs into one array texture.
Only update world probe if world changes.
Diffstat (limited to 'source/blender/draw/engines/eevee/eevee_materials.c')
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_materials.c | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c new file mode 100644 index 00000000000..23f87a41b24 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -0,0 +1,434 @@ +/* + * Copyright 2016, Blender Foundation. + * + * 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. + * + * Contributor(s): Blender Institute + * + */ + +/** \file eevee_materials.c + * \ingroup draw_engine + */ + +#include "DRW_render.h" + +#include "DNA_world_types.h" + +#include "BLI_dynstr.h" + +#include "GPU_material.h" + +#include "eevee_engine.h" +#include "eevee_lut.h" +#include "eevee_private.h" + +#define SHADER_DEFINES \ + "#define EEVEE_ENGINE\n" \ + "#define MAX_LIGHT " STRINGIFY(MAX_LIGHT) "\n" \ + "#define MAX_SHADOW_CUBE " STRINGIFY(MAX_SHADOW_CUBE) "\n" \ + "#define MAX_SHADOW_MAP " STRINGIFY(MAX_SHADOW_MAP) "\n" \ + "#define MAX_SHADOW_CASCADE " STRINGIFY(MAX_SHADOW_CASCADE) "\n" \ + "#define MAX_CASCADE_NUM " STRINGIFY(MAX_CASCADE_NUM) "\n" + +/* World shader variations */ +enum { + VAR_WORLD_BACKGROUND, + VAR_WORLD_PROBE, +}; + +/* Material shader variations */ +enum { + VAR_MAT_MESH = (1 << 0), + VAR_MAT_PROBE = (1 << 1), +}; + +/* *********** STATIC *********** */ +static struct { + char *frag_shader_lib; + + struct GPUShader *default_lit; + struct GPUShader *default_lit_flat; + + struct GPUShader *default_background; + + /* 64*64 array texture containing all LUTs and other utilitarian arrays. + * Packing enables us to same precious textures slots. */ + struct GPUTexture *util_tex; +} e_data = {NULL}; /* Engine data */ + +extern char datatoc_default_frag_glsl[]; +extern char datatoc_default_world_frag_glsl[]; +extern char datatoc_ltc_lib_glsl[]; +extern char datatoc_bsdf_lut_frag_glsl[]; +extern char datatoc_bsdf_common_lib_glsl[]; +extern char datatoc_bsdf_direct_lib_glsl[]; +extern char datatoc_bsdf_sampling_lib_glsl[]; +extern char datatoc_lit_surface_frag_glsl[]; +extern char datatoc_lit_surface_vert_glsl[]; +extern char datatoc_shadow_frag_glsl[]; +extern char datatoc_shadow_geom_glsl[]; +extern char datatoc_shadow_vert_glsl[]; +extern char datatoc_probe_filter_frag_glsl[]; +extern char datatoc_probe_sh_frag_glsl[]; +extern char datatoc_probe_geom_glsl[]; +extern char datatoc_probe_vert_glsl[]; +extern char datatoc_background_vert_glsl[]; + +extern Material defmaterial; +extern GlobalsUboStorage ts; + +/* *********** FUNCTIONS *********** */ + +#if 0 /* Used only to generate the LUT values */ +static struct GPUTexture *create_ggx_lut_texture(int UNUSED(w), int UNUSED(h)) +{ + struct GPUTexture *tex; + struct GPUFrameBuffer *fb = NULL; + static float samples_ct = 8192.0f; + static float inv_samples_ct = 1.0f / 8192.0f; + + char *lib_str = NULL; + + DynStr *ds_vert = BLI_dynstr_new(); + BLI_dynstr_append(ds_vert, datatoc_bsdf_common_lib_glsl); + BLI_dynstr_append(ds_vert, datatoc_bsdf_sampling_lib_glsl); + lib_str = BLI_dynstr_get_cstring(ds_vert); + BLI_dynstr_free(ds_vert); + + struct GPUShader *sh = DRW_shader_create_with_lib( + datatoc_probe_vert_glsl, datatoc_probe_geom_glsl, datatoc_bsdf_lut_frag_glsl, lib_str, + "#define HAMMERSLEY_SIZE 8192\n" + "#define BRDF_LUT_SIZE 64\n" + "#define NOISE_SIZE 64\n"); + + DRWPass *pass = DRW_pass_create("Probe Filtering", DRW_STATE_WRITE_COLOR); + DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_float(grp, "sampleCount", &samples_ct, 1); + DRW_shgroup_uniform_float(grp, "invSampleCount", &inv_samples_ct, 1); + DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley); + DRW_shgroup_uniform_texture(grp, "texJitter", e_data.jitter); + + struct Batch *geom = DRW_cache_fullscreen_quad_get(); + DRW_shgroup_call_add(grp, geom, NULL); + + float *texels = MEM_mallocN(sizeof(float[2]) * w * h, "lut"); + + tex = DRW_texture_create_2D(w, h, DRW_TEX_RG_16, DRW_TEX_FILTER, (float *)texels); + + DRWFboTexture tex_filter = {&tex, DRW_TEX_RG_16, DRW_TEX_FILTER}; + DRW_framebuffer_init(&fb, &draw_engine_eevee_type, w, h, &tex_filter, 1); + + DRW_framebuffer_bind(fb); + DRW_draw_pass(pass); + + float *data = MEM_mallocN(sizeof(float[3]) * w * h, "lut"); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glReadPixels(0, 0, w, h, GL_RGB, GL_FLOAT, data); + + printf("{"); + for (int i = 0; i < w*h * 3; i+=3) { + printf("%ff, %ff, ", data[i], data[i+1]); i+=3; + printf("%ff, %ff, ", data[i], data[i+1]); i+=3; + printf("%ff, %ff, ", data[i], data[i+1]); i+=3; + printf("%ff, %ff, \n", data[i], data[i+1]); + } + printf("}"); + + MEM_freeN(texels); + MEM_freeN(data); + + return tex; +} +#endif + +void EEVEE_materials_init(void) +{ + if (!e_data.frag_shader_lib) { + char *frag_str = NULL; + + /* Shaders */ + DynStr *ds_frag = BLI_dynstr_new(); + BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_ltc_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_bsdf_direct_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_lit_surface_frag_glsl); + e_data.frag_shader_lib = BLI_dynstr_get_cstring(ds_frag); + BLI_dynstr_free(ds_frag); + + ds_frag = BLI_dynstr_new(); + BLI_dynstr_append(ds_frag, e_data.frag_shader_lib); + BLI_dynstr_append(ds_frag, datatoc_default_frag_glsl); + frag_str = BLI_dynstr_get_cstring(ds_frag); + BLI_dynstr_free(ds_frag); + + e_data.default_background = DRW_shader_create_fullscreen( + datatoc_default_world_frag_glsl, NULL); + + e_data.default_lit = DRW_shader_create( + datatoc_lit_surface_vert_glsl, NULL, frag_str, + SHADER_DEFINES + "#define MESH_SHADER\n"); + e_data.default_lit_flat = DRW_shader_create( + datatoc_lit_surface_vert_glsl, NULL, frag_str, + SHADER_DEFINES + "#define MESH_SHADER\n" + "#define USE_FLAT_NORMAL\n"); + + MEM_freeN(frag_str); + + /* Textures */ + const int layers = 3; + float (*texels)[4] = MEM_mallocN(sizeof(float[4]) * 64 * 64 * layers, "utils texels"); + float (*texels_layer)[4] = texels; + + /* Copy ltc_mat_ggx into 1st layer */ + memcpy(texels_layer, ltc_mat_ggx, sizeof(float[4]) * 64 * 64); + texels_layer += 64 * 64; + + /* Copy bsdf_split_sum_ggx into 2nd layer red and green channels. + Copy ltc_mag_ggx into 2nd layer blue channel. */ + for (int i = 0; i < 64 * 64; i++) { + texels_layer[i][0] = bsdf_split_sum_ggx[i*2 + 0]; + texels_layer[i][1] = bsdf_split_sum_ggx[i*2 + 1]; + texels_layer[i][2] = ltc_mag_ggx[i]; + } + texels_layer += 64 * 64; + + /* Copy ltc_mag_ggx into 2nd layer blue channel */ + for (int i = 0; i < 64 * 64; i++) { + texels_layer[i][0] = blue_noise[i*3 + 0]; + texels_layer[i][1] = blue_noise[i*3 + 1]; + texels_layer[i][2] = blue_noise[i*3 + 2]; + } + + e_data.util_tex = DRW_texture_create_2D_array(64, 64, layers, DRW_TEX_RGBA_16, DRW_TEX_FILTER, (float *)texels); + MEM_freeN(texels); + } +} + +struct GPUMaterial *EEVEE_material_world_probe_get(struct Scene *scene, World *wo) +{ + return GPU_material_from_nodetree( + scene, wo->nodetree, &wo->gpumaterial, &DRW_engine_viewport_eevee_type, + VAR_WORLD_PROBE, + datatoc_probe_vert_glsl, datatoc_probe_geom_glsl, e_data.frag_shader_lib, + SHADER_DEFINES "#define PROBE_CAPTURE\n"); +} + +struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, World *wo) +{ + return GPU_material_from_nodetree( + scene, wo->nodetree, &wo->gpumaterial, &DRW_engine_viewport_eevee_type, + VAR_WORLD_BACKGROUND, + datatoc_background_vert_glsl, NULL, e_data.frag_shader_lib, + SHADER_DEFINES "#define WORLD_BACKGROUND\n"); +} + +struct GPUMaterial *EEVEE_material_mesh_probe_get(struct Scene *scene, Material *ma) +{ + return GPU_material_from_nodetree( + scene, ma->nodetree, &ma->gpumaterial, &DRW_engine_viewport_eevee_type, + VAR_MAT_MESH | VAR_MAT_PROBE, + datatoc_probe_vert_glsl, datatoc_probe_geom_glsl, e_data.frag_shader_lib, + SHADER_DEFINES "#define MESH_SHADER\n" "#define PROBE_CAPTURE\n"); +} + +struct GPUMaterial *EEVEE_material_mesh_get(struct Scene *scene, Material *ma) +{ + return GPU_material_from_nodetree( + scene, ma->nodetree, &ma->gpumaterial, &DRW_engine_viewport_eevee_type, + VAR_MAT_MESH, + datatoc_lit_surface_vert_glsl, NULL, e_data.frag_shader_lib, + SHADER_DEFINES "#define MESH_SHADER\n"); +} + +static void add_standard_uniforms(DRWShadingGroup *shgrp, EEVEE_SceneLayerData *sldata) +{ + DRW_shgroup_uniform_block(shgrp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(shgrp, "shadow_block", sldata->shadow_ubo); + DRW_shgroup_uniform_int(shgrp, "light_count", &sldata->lamps->num_light, 1); + DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex); + DRW_shgroup_uniform_buffer(shgrp, "probeCubes", &sldata->probe_pool); + DRW_shgroup_uniform_float(shgrp, "lodMax", &sldata->probes->lodmax, 1); + DRW_shgroup_uniform_buffer(shgrp, "shadowCubes", &sldata->shadow_depth_cube_pool); + DRW_shgroup_uniform_buffer(shgrp, "shadowCascades", &sldata->shadow_depth_cascade_pool); +} + +void EEVEE_materials_cache_init(EEVEE_Data *vedata) +{ + EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; + EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; + EEVEE_SceneLayerData *sldata = EEVEE_scene_layer_data_get(); + + { + psl->background_pass = DRW_pass_create("Background Pass", DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR); + + struct Batch *geom = DRW_cache_fullscreen_quad_get(); + DRWShadingGroup *grp = NULL; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + World *wo = scene->world; + + float *col = ts.colorBackground; + + if (wo) { + col = &wo->horr; + + if (wo->use_nodes && wo->nodetree) { + struct GPUMaterial *gpumat = EEVEE_material_world_background_get(scene, wo); + grp = DRW_shgroup_material_create(gpumat, psl->background_pass); + + if (grp) { + DRW_shgroup_call_add(grp, geom, NULL); + } + else { + /* Shader failed : pink background */ + static float pink[3] = {1.0f, 0.0f, 1.0f}; + col = pink; + } + } + } + + /* Fallback if shader fails or if not using nodetree. */ + if (grp == NULL) { + grp = DRW_shgroup_create(e_data.default_background, psl->background_pass); + DRW_shgroup_uniform_vec3(grp, "color", col, 1); + DRW_shgroup_call_add(grp, geom, NULL); + } + } + + { + struct GPUShader *depth_sh = DRW_shader_create_3D_depth_only(); + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + psl->depth_pass = DRW_pass_create("Depth Pass", state); + stl->g_data->depth_shgrp = DRW_shgroup_create(depth_sh, psl->depth_pass); + + state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_CULL_BACK; + psl->depth_pass_cull = DRW_pass_create("Depth Pass Cull", state); + stl->g_data->depth_shgrp_cull = DRW_shgroup_create(depth_sh, psl->depth_pass_cull); + } + + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; + psl->default_pass = DRW_pass_create("Default Lit Pass", state); + DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.default_lit, psl->default_pass); + add_standard_uniforms(shgrp, sldata); + + psl->default_flat_pass = DRW_pass_create("Default Flat Lit Pass", state); + shgrp = DRW_shgroup_create(e_data.default_lit_flat, psl->default_flat_pass); + add_standard_uniforms(shgrp, sldata); + } + + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; + psl->material_pass = DRW_pass_create("Material Shader Pass", state); + } +} + +/* Macro to call the right */ +#define ADD_MATERIAL_CALL(shgrp, ob, geom) do { \ + if (is_sculpt_mode) { \ + DRW_shgroup_call_sculpt_add(shgrp, ob, ob->obmat); \ + } \ + else { \ + DRW_shgroup_call_object_add(shgrp, geom, ob); \ + } \ +} while (0) + +void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sldata, Object *ob, struct Batch *geom) +{ + EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; + EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + + 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_active = (ob == draw_ctx->obact); + const bool is_sculpt_mode = is_active && (ob->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; + ADD_MATERIAL_CALL(depth_shgrp, ob, geom); + + /* Get per-material split surface */ + struct Batch **mat_geom = DRW_cache_object_surface_material_get(ob); + if (mat_geom) { + struct GPUShader *default_shader = e_data.default_lit; + struct DRWPass *default_pass = psl->default_pass; + + if (is_default_mode_shader) { + if (is_sculpt_mode) { + bool use_flat = DRW_object_is_flat_normal(ob); + default_shader = use_flat ? e_data.default_lit_flat : e_data.default_lit; + } + } + + for (int i = 0; i < MAX2(1, (is_sculpt_mode ? 1 : ob->totcol)); ++i) { + DRWShadingGroup *shgrp = NULL; + Material *ma = give_current_material(ob, i + 1); + + if (ma == NULL) + ma = &defmaterial; + + float *color_p = &ma->r; + float *metal_p = &ma->ray_mirror; + float *spec_p = &ma->spec; + float *rough_p = &ma->gloss_mir; + + if (ma->use_nodes && ma->nodetree) { + Scene *scene = draw_ctx->scene; + struct GPUMaterial *gpumat = EEVEE_material_mesh_get(scene, ma); + + shgrp = DRW_shgroup_material_create(gpumat, psl->material_pass); + if (shgrp) { + add_standard_uniforms(shgrp, sldata); + + ADD_MATERIAL_CALL(shgrp, ob, mat_geom[i]); + } + else { + /* Shader failed : pink color */ + static float col[3] = {1.0f, 0.0f, 1.0f}; + static float half = 0.5f; + + color_p = col; + metal_p = spec_p = rough_p = ½ + } + } + + /* Fallback to default shader */ + if (shgrp == NULL) { + shgrp = DRW_shgroup_create(default_shader, default_pass); + DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1); + DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1); + DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1); + DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1); + + ADD_MATERIAL_CALL(shgrp, ob, mat_geom[i]); + } + } + } +} + +void EEVEE_materials_free(void) +{ + MEM_SAFE_FREE(e_data.frag_shader_lib); + DRW_SHADER_FREE_SAFE(e_data.default_lit); + DRW_SHADER_FREE_SAFE(e_data.default_lit_flat); + DRW_SHADER_FREE_SAFE(e_data.default_background); + DRW_TEXTURE_FREE_SAFE(e_data.util_tex); +}
\ No newline at end of file |