From b9f6d033beecd75398be14419d37d8aab0609812 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 11 Mar 2020 14:58:19 +0100 Subject: Eevee: internal support for arbitrary number of volume grids This has no user visible impact yet since smoke volumes only support a fixed set of attributes, but will become important with the new volume object. For GPU shader compilation, volume grids are now handled separately from image textures. They are somewhere between a vertex attribute and an image texture, basically an attribute that is stored as a texture. Differential Revision: https://developer.blender.org/D6952 --- source/blender/gpu/GPU_material.h | 29 +++++--- source/blender/gpu/intern/gpu_codegen.c | 28 +++----- source/blender/gpu/intern/gpu_material.c | 34 +++++++--- source/blender/gpu/intern/gpu_node_graph.c | 69 +++++++++++++++++++ source/blender/gpu/intern/gpu_node_graph.h | 8 +++ .../material/gpu_shader_material_volume_info.glsl | 78 ++++++---------------- 6 files changed, 149 insertions(+), 97 deletions(-) (limited to 'source/blender/gpu') diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 7107748e62a..da00306bf9f 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -98,13 +98,9 @@ typedef enum eGPUBuiltin { GPU_LOC_TO_VIEW_MATRIX = (1 << 13), GPU_INVERSE_LOC_TO_VIEW_MATRIX = (1 << 14), GPU_OBJECT_INFO = (1 << 15), - GPU_VOLUME_DENSITY = (1 << 16), - GPU_VOLUME_COLOR = (1 << 17), - GPU_VOLUME_FLAME = (1 << 18), - GPU_VOLUME_TEMPERATURE = (1 << 19), - GPU_BARYCENTRIC_TEXCO = (1 << 20), - GPU_BARYCENTRIC_DIST = (1 << 21), - GPU_WORLD_NORMAL = (1 << 22), + GPU_BARYCENTRIC_TEXCO = (1 << 16), + GPU_BARYCENTRIC_DIST = (1 << 17), + GPU_WORLD_NORMAL = (1 << 18), } eGPUBuiltin; typedef enum eGPUMatFlag { @@ -146,6 +142,7 @@ GPUNodeLink *GPU_image(GPUMaterial *mat, struct Image *ima, struct ImageUser *iu GPUNodeLink *GPU_image_tiled(GPUMaterial *mat, struct Image *ima, struct ImageUser *iuser); GPUNodeLink *GPU_image_tiled_mapping(GPUMaterial *mat, struct Image *ima, struct ImageUser *iuser); GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *layer); +GPUNodeLink *GPU_volume_grid(GPUMaterial *mat, const char *name); GPUNodeLink *GPU_builtin(eGPUBuiltin builtin); bool GPU_link(GPUMaterial *mat, const char *name, ...); @@ -179,7 +176,8 @@ GPUMaterial *GPU_material_from_nodetree(struct Scene *scene, struct bNodeTree *ntree, struct ListBase *gpumaterials, const void *engine_type, - int options, + const int options, + const bool is_volume_shader, const char *vert_code, const char *geom_code, const char *frag_lib, @@ -192,7 +190,6 @@ void GPU_materials_free(struct Main *bmain); struct Scene *GPU_material_scene(GPUMaterial *material); struct GPUPass *GPU_material_get_pass(GPUMaterial *material); -struct ListBase *GPU_material_get_inputs(GPUMaterial *material); struct Material *GPU_material_get_material(GPUMaterial *material); eGPUMaterialStatus GPU_material_status(GPUMaterial *mat); @@ -200,8 +197,10 @@ struct GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material); void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs); struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void); -bool GPU_material_use_domain_surface(GPUMaterial *mat); -bool GPU_material_use_domain_volume(GPUMaterial *mat); +bool GPU_material_has_surface_output(GPUMaterial *mat); +bool GPU_material_has_volume_output(GPUMaterial *mat); + +bool GPU_material_is_volume_shader(GPUMaterial *mat); void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag); bool GPU_material_flag_get(GPUMaterial *mat, eGPUMatFlag flag); @@ -231,8 +230,16 @@ typedef struct GPUMaterialTexture { int users; } GPUMaterialTexture; +typedef struct GPUMaterialVolumeGrid { + struct GPUMaterialVolumeGrid *next, *prev; + char *name; + char sampler_name[32]; /* Name of sampler in GLSL. */ + int users; +} GPUMaterialVolumeGrid; + ListBase GPU_material_attributes(GPUMaterial *material); ListBase GPU_material_textures(GPUMaterial *material); +ListBase GPU_material_volume_grids(GPUMaterial *material); #ifdef __cplusplus } diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index e8616c1e256..bcaa95c2f59 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -269,18 +269,6 @@ static const char *gpu_builtin_name(eGPUBuiltin builtin) else if (builtin == GPU_OBJECT_INFO) { return "unfobjectinfo"; } - else if (builtin == GPU_VOLUME_DENSITY) { - return "sampdensity"; - } - else if (builtin == GPU_VOLUME_COLOR) { - return "sampcolor"; - } - else if (builtin == GPU_VOLUME_FLAME) { - return "sampflame"; - } - else if (builtin == GPU_VOLUME_TEMPERATURE) { - return "unftemperature"; - } else if (builtin == GPU_BARYCENTRIC_TEXCO) { return "unfbarycentrictex"; } @@ -344,6 +332,11 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, } } + /* Volume Grids */ + for (GPUMaterialVolumeGrid *grid = graph->volume_grids.first; grid; grid = grid->next) { + BLI_dynstr_appendf(ds, "uniform sampler3D %s;\n", grid->sampler_name); + } + /* Print other uniforms */ for (node = graph->nodes.first; node; node = node->next) { for (input = node->inputs.first; input; input = input->next) { @@ -353,13 +346,7 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, builtins |= input->builtin; name = gpu_builtin_name(input->builtin); - if (BLI_str_startswith(name, "samp")) { - if ((input->builtin == GPU_VOLUME_DENSITY) || (input->builtin == GPU_VOLUME_COLOR) || - (input->builtin == GPU_VOLUME_FLAME)) { - BLI_dynstr_appendf(ds, "uniform sampler3D %s;\n", name); - } - } - else if (BLI_str_startswith(name, "unf")) { + if (BLI_str_startswith(name, "unf")) { BLI_dynstr_appendf(ds, "uniform %s %s;\n", gpu_data_type_to_string(input->type), name); } else { @@ -442,6 +429,9 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f else if (input->source == GPU_SOURCE_TEX_TILED_MAPPING) { BLI_dynstr_append(ds, input->texture->tiled_mapping_name); } + else if (input->source == GPU_SOURCE_VOLUME_GRID) { + BLI_dynstr_append(ds, input->volume_grid->sampler_name); + } else if (input->source == GPU_SOURCE_OUTPUT) { codegen_convert_datatype( ds, input->link->output->type, input->type, "tmp", input->link->output->id); diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index b2658839d67..8a4a88d0663 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -70,6 +70,7 @@ struct GPUMaterial { const void *engine_type; /* attached engine type */ int options; /* to identify shader variations (shadow, probe, world background...) */ + bool is_volume_shader; /* is volumetric shader */ /* Nodes */ GPUNodeGraph graph; @@ -80,7 +81,8 @@ struct GPUMaterial { /* XXX: Should be in Material. But it depends on the output node * used and since the output selection is different for GPUMaterial... */ - int domain; + bool has_volume_output; + bool has_surface_output; /* Only used by Eevee to know which bsdf are used. */ int flag; @@ -109,8 +111,8 @@ struct GPUMaterial { }; enum { - GPU_DOMAIN_SURFACE = (1 << 0), - GPU_DOMAIN_VOLUME = (1 << 1), + GPU_USE_SURFACE_OUTPUT = (1 << 0), + GPU_USE_VOLUME_OUTPUT = (1 << 1), }; /* Functions */ @@ -567,6 +569,11 @@ ListBase GPU_material_textures(GPUMaterial *material) return material->graph.textures; } +ListBase GPU_material_volume_grids(GPUMaterial *material) +{ + return material->graph.volume_grids; +} + void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link) { if (!material->graph.outlink) { @@ -592,14 +599,19 @@ eGPUMaterialStatus GPU_material_status(GPUMaterial *mat) /* Code generation */ -bool GPU_material_use_domain_surface(GPUMaterial *mat) +bool GPU_material_has_surface_output(GPUMaterial *mat) +{ + return mat->has_surface_output; +} + +bool GPU_material_has_volume_output(GPUMaterial *mat) { - return (mat->domain & GPU_DOMAIN_SURFACE); + return mat->has_volume_output; } -bool GPU_material_use_domain_volume(GPUMaterial *mat) +bool GPU_material_is_volume_shader(GPUMaterial *mat) { - return (mat->domain & GPU_DOMAIN_VOLUME); + return mat->is_volume_shader; } void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag) @@ -636,7 +648,8 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, struct bNodeTree *ntree, ListBase *gpumaterials, const void *engine_type, - int options, + const int options, + const bool is_volume_shader, const char *vert_code, const char *geom_code, const char *frag_lib, @@ -655,6 +668,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, mat->scene = scene; mat->engine_type = engine_type; mat->options = options; + mat->is_volume_shader = is_volume_shader; #ifndef NDEBUG BLI_snprintf(mat->name, sizeof(mat->name), "%s", name); #else @@ -670,8 +684,8 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, gpu_material_ramp_texture_build(mat); - SET_FLAG_FROM_TEST(mat->domain, has_surface_output, GPU_DOMAIN_SURFACE); - SET_FLAG_FROM_TEST(mat->domain, has_volume_output, GPU_DOMAIN_VOLUME); + mat->has_surface_output = has_surface_output; + mat->has_volume_output = has_volume_output; if (mat->graph.outlink) { /* HACK: this is only for eevee. We add the define here after the nodetree evaluation. */ diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index de35b43109f..0d5cc46c0b9 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -119,6 +119,10 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType input->source = GPU_SOURCE_TEX_TILED_MAPPING; input->texture = link->texture; break; + case GPU_NODE_LINK_VOLUME_GRID: + input->source = GPU_SOURCE_VOLUME_GRID; + input->volume_grid = link->volume_grid; + break; case GPU_NODE_LINK_ATTR: input->source = GPU_SOURCE_ATTR; input->attr = link->attr; @@ -321,6 +325,31 @@ static GPUMaterialTexture *gpu_node_graph_add_texture(GPUNodeGraph *graph, return tex; } +static GPUMaterialVolumeGrid *gpu_node_graph_add_volume_grid(GPUNodeGraph *graph, const char *name) +{ + /* Find existing volume grid. */ + int num_grids = 0; + GPUMaterialVolumeGrid *grid = graph->volume_grids.first; + for (; grid; grid = grid->next) { + if (STREQ(grid->name, name)) { + break; + } + num_grids++; + } + + /* Add new requested volume grid. */ + if (grid == NULL) { + grid = MEM_callocN(sizeof(*grid), __func__); + grid->name = BLI_strdup(name); + BLI_snprintf(grid->sampler_name, sizeof(grid->sampler_name), "vsamp%d", num_grids); + BLI_addtail(&graph->volume_grids, grid); + } + + grid->users++; + + return grid; +} + /* Creating Inputs */ GPUNodeLink *GPU_attribute(GPUMaterial *mat, const CustomDataType type, const char *name) @@ -394,6 +423,30 @@ GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *ro return link; } +GPUNodeLink *GPU_volume_grid(GPUMaterial *mat, const char *name) +{ + /* NOTE: this could be optimized by automatically merging duplicate + * lookups of the same attribute. */ + GPUNodeGraph *graph = gpu_material_node_graph(mat); + GPUNodeLink *link = gpu_node_link_create(); + link->link_type = GPU_NODE_LINK_VOLUME_GRID; + link->volume_grid = gpu_node_graph_add_volume_grid(graph, name); + + /* Two special cases, where we adjust the output values of smoke grids to + * bring the into standard range without having to modify the grid values. */ + if (strcmp(name, "color") == 0) { + GPU_link(mat, "node_attribute_volume_color", link, &link); + } + else if (strcmp(name, "temperature") == 0) { + GPU_link(mat, "node_attribute_volume_temperature", link, &link); + } + else { + GPU_link(mat, "node_attribute_volume", link, &link); + } + + return link; +} + GPUNodeLink *GPU_builtin(eGPUBuiltin builtin) { GPUNodeLink *link = gpu_node_link_create(); @@ -537,6 +590,9 @@ static void gpu_inputs_free(ListBase *inputs) else if (ELEM(input->source, GPU_SOURCE_TEX, GPU_SOURCE_TEX_TILED_MAPPING)) { input->texture->users--; } + else if (ELEM(input->source, GPU_SOURCE_VOLUME_GRID)) { + input->volume_grid->users--; + } if (input->link) { gpu_node_link_free(input->link); @@ -579,6 +635,11 @@ void gpu_node_graph_free_nodes(GPUNodeGraph *graph) void gpu_node_graph_free(GPUNodeGraph *graph) { gpu_node_graph_free_nodes(graph); + + for (GPUMaterialVolumeGrid *grid = graph->volume_grids.first; grid; grid = grid->next) { + MEM_SAFE_FREE(grid->name); + } + BLI_freelistN(&graph->volume_grids); BLI_freelistN(&graph->textures); BLI_freelistN(&graph->attributes); } @@ -637,4 +698,12 @@ void gpu_node_graph_prune_unused(GPUNodeGraph *graph) BLI_freelinkN(&graph->textures, tex); } } + + for (GPUMaterialVolumeGrid *grid = graph->volume_grids.first, *next = NULL; grid; grid = next) { + next = grid->next; + if (grid->users == 0) { + MEM_SAFE_FREE(grid->name); + BLI_freelinkN(&graph->volume_grids, grid); + } + } } diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 9214bd7f1ba..ceaeea2bfa8 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -48,6 +48,8 @@ typedef enum eGPUDataSource { GPU_SOURCE_STRUCT, GPU_SOURCE_TEX, GPU_SOURCE_TEX_TILED_MAPPING, + GPU_SOURCE_VOLUME_GRID, + GPU_SOURCE_VOLUME_GRID_TRANSFORM, } eGPUDataSource; typedef enum { @@ -59,6 +61,7 @@ typedef enum { GPU_NODE_LINK_IMAGE, GPU_NODE_LINK_IMAGE_TILED, GPU_NODE_LINK_IMAGE_TILED_MAPPING, + GPU_NODE_LINK_VOLUME_GRID, GPU_NODE_LINK_OUTPUT, GPU_NODE_LINK_UNIFORM, } GPUNodeLinkType; @@ -88,6 +91,8 @@ struct GPUNodeLink { eGPUBuiltin builtin; /* GPU_NODE_LINK_COLORBAND */ struct GPUTexture **colorband; + /* GPU_NODE_LINK_VOLUME_GRID */ + struct GPUMaterialVolumeGrid *volume_grid; /* GPU_NODE_LINK_OUTPUT */ struct GPUOutput *output; /* GPU_NODE_LINK_ATTR */ @@ -126,6 +131,8 @@ typedef struct GPUInput { struct GPUMaterialTexture *texture; /* GPU_SOURCE_ATTR */ struct GPUMaterialAttribute *attr; + /* GPU_SOURCE_VOLUME_GRID */ + struct GPUMaterialVolumeGrid *volume_grid; }; } GPUInput; @@ -139,6 +146,7 @@ typedef struct GPUNodeGraph { /* Requested attributes and textures. */ ListBase attributes; ListBase textures; + ListBase volume_grids; } GPUNodeGraph; /* Node Graph */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl index 0fecb5bd1f9..a80cd3cb329 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl @@ -1,18 +1,21 @@ -void node_attribute_volume_density(sampler3D tex, out vec4 outcol, out vec3 outvec, out float outf) + +/* Uniforms to convert smoke grid values into standard range. */ +uniform vec3 volumeColor = vec3(1.0); +uniform vec2 volumeTemperature = vec2(0.0); + +/* Generic volume attribute. */ +void node_attribute_volume(sampler3D tex, out vec3 outvec) { #if defined(MESH_SHADER) && defined(VOLUMETRICS) vec3 cos = volumeObjectLocalCoord; #else vec3 cos = vec3(0.0); #endif - outf = texture(tex, cos).r; - outvec = vec3(outf, outf, outf); - outcol = vec4(outf, outf, outf, 1.0); + outvec = texture(tex, cos).rgb; } -uniform vec3 volumeColor = vec3(1.0); - -void node_attribute_volume_color(sampler3D tex, out vec4 outcol, out vec3 outvec, out float outf) +/* Special color attribute for smoke. */ +void node_attribute_volume_color(sampler3D tex, out vec3 outvec) { #if defined(MESH_SHADER) && defined(VOLUMETRICS) vec3 cos = volumeObjectLocalCoord; @@ -20,70 +23,31 @@ void node_attribute_volume_color(sampler3D tex, out vec4 outcol, out vec3 outvec vec3 cos = vec3(0.0); #endif - vec4 value = texture(tex, cos).rgba; /* Density is premultiplied for interpolation, divide it out here. */ + vec4 value = texture(tex, cos).rgba; if (value.a > 1e-8) { value.rgb /= value.a; } outvec = value.rgb * volumeColor; - outcol = vec4(outvec, 1.0); - outf = avg(outvec); -} - -void node_attribute_volume_flame(sampler3D tex, out vec4 outcol, out vec3 outvec, out float outf) -{ -#if defined(MESH_SHADER) && defined(VOLUMETRICS) - vec3 cos = volumeObjectLocalCoord; -#else - vec3 cos = vec3(0.0); -#endif - outf = texture(tex, cos).r; - outvec = vec3(outf, outf, outf); - outcol = vec4(outf, outf, outf, 1.0); } -void node_attribute_volume_temperature( - sampler3D tex, vec2 temperature, out vec4 outcol, out vec3 outvec, out float outf) +/* Special temperature attribute for smoke. */ +void node_attribute_volume_temperature(sampler3D tex, out float outf) { #if defined(MESH_SHADER) && defined(VOLUMETRICS) vec3 cos = volumeObjectLocalCoord; #else vec3 cos = vec3(0.0); #endif - float flame = texture(tex, cos).r; - - outf = (flame > 0.01) ? temperature.x + flame * (temperature.y - temperature.x) : 0.0; - outvec = vec3(outf, outf, outf); - outcol = vec4(outf, outf, outf, 1.0); -} -void node_volume_info(sampler3D densitySampler, - sampler3D colorSampler, - sampler3D flameSampler, - vec2 temperature, - out vec4 outColor, - out float outDensity, - out float outFlame, - out float outTemprature) -{ -#if defined(MESH_SHADER) && defined(VOLUMETRICS) - vec3 p = volumeObjectLocalCoord; -#else - vec3 p = vec3(0.0); -#endif - - outDensity = texture(densitySampler, p).r; - - /* Color is premultiplied for interpolation, divide it out here. */ - vec4 color = texture(colorSampler, p); - if (color.a > 1e-8) { - color.rgb /= color.a; + float value = texture(tex, cos).r; + if (volumeTemperature.x < volumeTemperature.y) { + outf = (value > 0.01) ? + volumeTemperature.x + value * (volumeTemperature.y - volumeTemperature.x) : + 0.0; + } + else { + outf = value; } - outColor = vec4(color.rgb * volumeColor, 1.0); - - float flame = texture(flameSampler, p).r; - outFlame = flame; - - outTemprature = (flame > 0.01) ? temperature.x + flame * (temperature.y - temperature.x) : 0.0; } -- cgit v1.2.3