Eevee: Add support for Nishita sky texture

Sun Disc is currently not supported because it'll need special handling - on the one hand, I'm not sure if Eevee would handle a 1e6 coming out of a background shader without issues, and on the other hand it won't actually cast sharp shadows anyways.
I guess we'd want to internally add a sun to the lamps if Sun Disc is enabled, but getting that right is tricky since the user could e.g. swap RGB channels in the node tree and the lamp wouldn't match that.
Anyways, that can be handled later, the sky itself is already a start.

Reviewed By: fclem

Differential Revision: https://developer.blender.org/D13522
This commit is contained in:
Lukas Stockner 2022-09-16 15:04:47 +02:00 committed by Jeroen Bakker
parent 2eb19eeb19
commit 44aaa9893b
10 changed files with 218 additions and 10 deletions

View File

@ -1631,6 +1631,10 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial
/* Color Ramp */
DRW_shgroup_uniform_texture(grp, tex->sampler_name, *tex->colorband);
}
else if (tex->sky) {
/* Sky */
DRW_shgroup_uniform_texture_ex(grp, tex->sampler_name, *tex->sky, tex->sampler_state);
}
}
GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material);

View File

@ -162,6 +162,12 @@ GPUNodeLink *GPU_image_tiled(GPUMaterial *mat,
struct ImageUser *iuser,
eGPUSamplerState sampler_state);
GPUNodeLink *GPU_image_tiled_mapping(GPUMaterial *mat, struct Image *ima, struct ImageUser *iuser);
GPUNodeLink *GPU_image_sky(GPUMaterial *mat,
int width,
int height,
const float *pixels,
float *layer,
eGPUSamplerState sampler_state);
GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *row);
/**
@ -297,6 +303,7 @@ typedef struct GPUMaterialTexture {
struct ImageUser iuser;
bool iuser_available;
struct GPUTexture **colorband;
struct GPUTexture **sky;
char sampler_name[32]; /* Name of sampler in GLSL. */
char tiled_mapping_name[32]; /* Name of tile mapping sampler in GLSL. */
int users;

View File

@ -386,6 +386,10 @@ void GPUCodegen::generate_resources()
const char *name = info.name_buffer.append_sampler_name(tex->sampler_name);
info.sampler(slot++, ImageType::FLOAT_1D_ARRAY, name, Frequency::BATCH);
}
else if (tex->sky) {
const char *name = info.name_buffer.append_sampler_name(tex->sampler_name);
info.sampler(0, ImageType::FLOAT_2D_ARRAY, name, Frequency::BATCH);
}
else if (tex->tiled_mapping_name[0] != '\0') {
const char *name = info.name_buffer.append_sampler_name(tex->sampler_name);
info.sampler(slot++, ImageType::FLOAT_2D_ARRAY, name, Frequency::BATCH);

View File

@ -41,12 +41,18 @@
/* Structs */
#define MAX_COLOR_BAND 128
#define MAX_GPU_SKIES 8
typedef struct GPUColorBandBuilder {
float pixels[MAX_COLOR_BAND][CM_TABLE + 1][4];
int current_layer;
} GPUColorBandBuilder;
typedef struct GPUSkyBuilder {
float pixels[MAX_GPU_SKIES][GPU_SKY_WIDTH * GPU_SKY_HEIGHT][4];
int current_layer;
} GPUSkyBuilder;
struct GPUMaterial {
/* Contains GPUShader and source code for deferred compilation.
* Can be shared between similar material (i.e: sharing same nodetree topology). */
@ -73,6 +79,10 @@ struct GPUMaterial {
GPUTexture *coba_tex;
/** Builder for coba_tex. */
GPUColorBandBuilder *coba_builder;
/** 2D Texture array containing all sky textures. */
GPUTexture *sky_tex;
/** Builder for sky_tex. */
GPUSkyBuilder *sky_builder;
/* Low level node graph(s). Also contains resources needed by the material. */
GPUNodeGraph graph;
@ -98,6 +108,35 @@ struct GPUMaterial {
/* Functions */
GPUTexture **gpu_material_sky_texture_layer_set(
GPUMaterial *mat, int width, int height, const float *pixels, float *row)
{
/* In order to put all sky textures into one 2D array texture,
* we need them to be the same size. */
BLI_assert(width == GPU_SKY_WIDTH);
BLI_assert(height == GPU_SKY_HEIGHT);
UNUSED_VARS_NDEBUG(width, height);
if (mat->sky_builder == NULL) {
mat->sky_builder = MEM_mallocN(sizeof(GPUSkyBuilder), "GPUSkyBuilder");
mat->sky_builder->current_layer = 0;
}
int layer = mat->sky_builder->current_layer;
*row = (float)layer;
if (*row == MAX_GPU_SKIES) {
printf("Too many sky textures in shader!\n");
}
else {
float *dst = (float *)mat->sky_builder->pixels[layer];
memcpy(dst, pixels, sizeof(float) * GPU_SKY_WIDTH * GPU_SKY_HEIGHT * 4);
mat->sky_builder->current_layer += 1;
}
return &mat->sky_tex;
}
GPUTexture **gpu_material_ramp_texture_row_set(GPUMaterial *mat,
int size,
float *pixels,
@ -143,6 +182,24 @@ static void gpu_material_ramp_texture_build(GPUMaterial *mat)
mat->coba_builder = NULL;
}
static void gpu_material_sky_texture_build(GPUMaterial *mat)
{
if (mat->sky_builder == NULL) {
return;
}
mat->sky_tex = GPU_texture_create_2d_array("mat_sky",
GPU_SKY_WIDTH,
GPU_SKY_HEIGHT,
mat->sky_builder->current_layer,
1,
GPU_RGBA32F,
(float *)mat->sky_builder->pixels);
MEM_freeN(mat->sky_builder);
mat->sky_builder = NULL;
}
void GPU_material_free_single(GPUMaterial *material)
{
bool do_free = atomic_sub_and_fetch_uint32(&material->refcount, 1) == 0;
@ -161,6 +218,9 @@ void GPU_material_free_single(GPUMaterial *material)
if (material->coba_tex != NULL) {
GPU_texture_free(material->coba_tex);
}
if (material->sky_tex != NULL) {
GPU_texture_free(material->sky_tex);
}
if (material->sss_profile != NULL) {
GPU_uniformbuf_free(material->sss_profile);
}
@ -684,6 +744,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
ntreeGPUMaterialNodes(localtree, mat);
gpu_material_ramp_texture_build(mat);
gpu_material_sky_texture_build(mat);
{
/* Create source code and search pass cache for an already compiled version. */

View File

@ -112,6 +112,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType
break;
case GPU_NODE_LINK_IMAGE:
case GPU_NODE_LINK_IMAGE_TILED:
case GPU_NODE_LINK_IMAGE_SKY:
case GPU_NODE_LINK_COLORBAND:
input->source = GPU_SOURCE_TEX;
input->texture = link->texture;
@ -438,6 +439,7 @@ static GPUMaterialTexture *gpu_node_graph_add_texture(GPUNodeGraph *graph,
Image *ima,
ImageUser *iuser,
struct GPUTexture **colorband,
struct GPUTexture **sky,
GPUNodeLinkType link_type,
eGPUSamplerState sampler_state)
{
@ -445,7 +447,8 @@ static GPUMaterialTexture *gpu_node_graph_add_texture(GPUNodeGraph *graph,
int num_textures = 0;
GPUMaterialTexture *tex = graph->textures.first;
for (; tex; tex = tex->next) {
if (tex->ima == ima && tex->colorband == colorband && tex->sampler_state == sampler_state) {
if (tex->ima == ima && tex->colorband == colorband && tex->sky == sky &&
tex->sampler_state == sampler_state) {
break;
}
num_textures++;
@ -460,6 +463,7 @@ static GPUMaterialTexture *gpu_node_graph_add_texture(GPUNodeGraph *graph,
tex->iuser_available = true;
}
tex->colorband = colorband;
tex->sky = sky;
tex->sampler_state = sampler_state;
BLI_snprintf(tex->sampler_name, sizeof(tex->sampler_name), "samp%d", num_textures);
if (ELEM(link_type, GPU_NODE_LINK_IMAGE_TILED, GPU_NODE_LINK_IMAGE_TILED_MAPPING)) {
@ -580,7 +584,24 @@ GPUNodeLink *GPU_image(GPUMaterial *mat,
GPUNodeLink *link = gpu_node_link_create();
link->link_type = GPU_NODE_LINK_IMAGE;
link->texture = gpu_node_graph_add_texture(
graph, ima, iuser, NULL, link->link_type, sampler_state);
graph, ima, iuser, NULL, NULL, link->link_type, sampler_state);
return link;
}
GPUNodeLink *GPU_image_sky(GPUMaterial *mat,
int width,
int height,
const float *pixels,
float *layer,
eGPUSamplerState sampler_state)
{
struct GPUTexture **sky = gpu_material_sky_texture_layer_set(mat, width, height, pixels, layer);
GPUNodeGraph *graph = gpu_material_node_graph(mat);
GPUNodeLink *link = gpu_node_link_create();
link->link_type = GPU_NODE_LINK_IMAGE_SKY;
link->texture = gpu_node_graph_add_texture(
graph, NULL, NULL, NULL, sky, link->link_type, sampler_state);
return link;
}
@ -593,7 +614,7 @@ GPUNodeLink *GPU_image_tiled(GPUMaterial *mat,
GPUNodeLink *link = gpu_node_link_create();
link->link_type = GPU_NODE_LINK_IMAGE_TILED;
link->texture = gpu_node_graph_add_texture(
graph, ima, iuser, NULL, link->link_type, sampler_state);
graph, ima, iuser, NULL, NULL, link->link_type, sampler_state);
return link;
}
@ -603,7 +624,7 @@ GPUNodeLink *GPU_image_tiled_mapping(GPUMaterial *mat, Image *ima, ImageUser *iu
GPUNodeLink *link = gpu_node_link_create();
link->link_type = GPU_NODE_LINK_IMAGE_TILED_MAPPING;
link->texture = gpu_node_graph_add_texture(
graph, ima, iuser, NULL, link->link_type, GPU_SAMPLER_MAX);
graph, ima, iuser, NULL, NULL, link->link_type, GPU_SAMPLER_MAX);
return link;
}
@ -616,7 +637,7 @@ GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *ro
GPUNodeLink *link = gpu_node_link_create();
link->link_type = GPU_NODE_LINK_COLORBAND;
link->texture = gpu_node_graph_add_texture(
graph, NULL, NULL, colorband, link->link_type, GPU_SAMPLER_MAX);
graph, NULL, NULL, colorband, NULL, link->link_type, GPU_SAMPLER_MAX);
return link;
}

View File

@ -47,6 +47,7 @@ typedef enum {
GPU_NODE_LINK_IMAGE,
GPU_NODE_LINK_IMAGE_TILED,
GPU_NODE_LINK_IMAGE_TILED_MAPPING,
GPU_NODE_LINK_IMAGE_SKY,
GPU_NODE_LINK_OUTPUT,
GPU_NODE_LINK_UNIFORM,
GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN,
@ -197,6 +198,11 @@ struct GPUTexture **gpu_material_ramp_texture_row_set(struct GPUMaterial *mat,
int size,
float *pixels,
float *row);
/**
* Returns the address of the future pointer to sky_tex
*/
struct GPUTexture **gpu_material_sky_texture_layer_set(
struct GPUMaterial *mat, int width, int height, const float *pixels, float *layer);
#ifdef __cplusplus
}

View File

@ -144,7 +144,47 @@ void node_tex_sky_hosekwilkie(vec3 co,
color = vec4(dot(xyz_to_r, xyz), dot(xyz_to_g, xyz), dot(xyz_to_b, xyz), 1);
}
void node_tex_sky_nishita(vec3 co, out vec4 color)
void node_tex_sky_nishita(vec3 co,
float sun_rotation,
vec3 xyz_to_r,
vec3 xyz_to_g,
vec3 xyz_to_b,
sampler2DArray ima,
float layer,
out vec4 color)
{
color = vec4(1.0);
vec3 spherical = sky_spherical_coordinates(co);
vec3 xyz;
if (co.z < -0.4) {
/* too far below the horizon, just return black */
color = vec4(0, 0, 0, 1);
}
else {
/* evaluate longitudinal position on the map */
float x = (spherical.y + M_PI + sun_rotation) / M_2PI;
if (x > 1.0) {
x -= 1.0;
}
float fade;
float y;
if (co.z < 0.0) {
/* we're below the horizon, so extend the map by blending from values at the horizon
* to zero according to a cubic falloff */
fade = 1.0 + co.z * 2.5;
fade = fade * fade * fade;
y = 0.0;
}
else {
/* we're above the horizon, so compute the lateral position by inverting the remapped
* coordinates that are preserve to have more detail near the horizon. */
fade = 1.0;
y = sqrt((M_PI_2 - spherical.x) / M_PI_2);
}
/* look up color in the precomputed map and convert to RGB */
xyz = fade * texture(ima, vec3(x, y, layer)).rgb;
color = vec4(dot(xyz_to_r, xyz), dot(xyz_to_g, xyz), dot(xyz_to_b, xyz), 1);
}
}

View File

@ -21,6 +21,9 @@ extern "C" {
#define CM_TOT 4
#define GPU_SKY_WIDTH 512
#define GPU_SKY_HEIGHT 128
typedef struct CurveMapPoint {
float x, y;
/** Shorty for result lookup. */

View File

@ -150,6 +150,22 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
if(WITH_TBB)
add_definitions(-DWITH_TBB)
if(WIN32)
# TBB includes Windows.h which will define min/max macros
# that will collide with the stl versions.
add_definitions(-DNOMINMAX)
endif()
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
)
list(APPEND LIB
${TBB_LIBRARIES}
)
endif()
blender_add_lib(bf_nodes_shader "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WITH_UNITY_BUILD)

View File

@ -4,6 +4,8 @@
#include "node_shader_util.hh"
#include "sky_model.h"
#include "BLI_task.hh"
#include "BKE_context.h"
#include "BKE_scene.h"
@ -36,7 +38,7 @@ static void node_shader_buts_tex_sky(uiLayout *layout, bContext *C, PointerRNA *
if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) {
Scene *scene = CTX_data_scene(C);
if (BKE_scene_uses_blender_eevee(scene)) {
uiItemL(layout, TIP_("Nishita not available in Eevee"), ICON_ERROR);
uiItemL(layout, TIP_("Sun disc not available in Eevee"), ICON_ERROR);
}
uiItemR(layout, ptr, "sun_disc", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0);
@ -179,7 +181,7 @@ static int node_shader_gpu_tex_sky(GPUMaterial *mat,
GPU_uniform(xyz_to_rgb.g),
GPU_uniform(xyz_to_rgb.b));
}
if (tex->sky_model == 1) {
else if (tex->sky_model == 1) {
/* Hosek / Wilkie */
sun_angles[0] = fmin(M_PI_2, sun_angles[0]); /* clamp to horizon */
SKY_ArHosekSkyModelState *sky_state = SKY_arhosek_xyz_skymodelstate_alloc_init(
@ -219,8 +221,52 @@ static int node_shader_gpu_tex_sky(GPUMaterial *mat,
GPU_uniform(xyz_to_rgb.g),
GPU_uniform(xyz_to_rgb.b));
}
else {
/* Nishita */
return GPU_stack_link(mat, node, "node_tex_sky_nishita", in, out);
Array<float> pixels(4 * GPU_SKY_WIDTH * GPU_SKY_HEIGHT);
threading::parallel_for(IndexRange(GPU_SKY_HEIGHT), 2, [&](IndexRange range) {
SKY_nishita_skymodel_precompute_texture(pixels.data(),
4,
range.first(),
range.one_after_last(),
GPU_SKY_WIDTH,
GPU_SKY_HEIGHT,
tex->sun_elevation,
tex->altitude,
tex->air_density,
tex->dust_density,
tex->ozone_density);
});
float sun_rotation = fmodf(tex->sun_rotation, 2.0f * M_PI);
if (sun_rotation < 0.0f) {
sun_rotation += 2.0f * M_PI;
}
sun_rotation = 2.0f * M_PI - sun_rotation;
XYZ_to_RGB xyz_to_rgb;
get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
eGPUSamplerState sampler = GPU_SAMPLER_REPEAT | GPU_SAMPLER_FILTER;
/* To fix pole issue we clamp the v coordinate. */
sampler &= ~GPU_SAMPLER_REPEAT_T;
float layer;
GPUNodeLink *sky_texture = GPU_image_sky(
mat, GPU_SKY_WIDTH, GPU_SKY_HEIGHT, pixels.data(), &layer, sampler);
return GPU_stack_link(mat,
node,
"node_tex_sky_nishita",
in,
out,
GPU_constant(&sun_rotation),
GPU_uniform(xyz_to_rgb.r),
GPU_uniform(xyz_to_rgb.g),
GPU_uniform(xyz_to_rgb.b),
sky_texture,
GPU_constant(&layer));
}
}
static void node_shader_update_sky(bNodeTree *ntree, bNode *node)