EEVEE-Next: Deferred Pipeline #105868
|
@ -649,7 +649,7 @@ void Film::accumulate(const DRWView *view, GPUTexture *combined_final_tx)
|
|||
|
||||
draw::View drw_view("MainView", view);
|
||||
|
||||
DRW_manager_get()->submit(accumulate_ps_, drw_view);
|
||||
inst_.manager->submit(accumulate_ps_, drw_view);
|
||||
|
||||
combined_tx_.swap();
|
||||
weight_tx_.swap();
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_material.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
@ -69,10 +70,11 @@ struct GBuffer {
|
|||
Texture closure_tx = {"GbufferClosure"};
|
||||
Texture color_tx = {"GbufferColor"};
|
||||
|
||||
void acquire(int2 extent, eClosureBits closures_used)
|
||||
void acquire(int2 extent, eClosureBits closure_bits_)
|
||||
{
|
||||
const bool use_sss = (closure_bits_ & CLOSURE_SSS) != 0;
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE;
|
||||
closure_tx.ensure_2d_array(GPU_RGBA16, extent, (closures_used & CLOSURE_SSS) ? 3 : 2, usage);
|
||||
closure_tx.ensure_2d_array(GPU_RGBA16, extent, use_sss ? 3 : 2, usage);
|
||||
color_tx.ensure_2d_array(GPU_RGB10_A2, extent, 2, usage);
|
||||
}
|
||||
|
||||
|
|
|
@ -240,10 +240,6 @@ Material &MaterialModule::material_sync(Object *ob,
|
|||
(has_motion ? MAT_PIPE_DEFERRED_PREPASS_VELOCITY :
|
||||
MAT_PIPE_DEFERRED_PREPASS);
|
||||
|
||||
/* TEST until we have deferred pipeline up and running. */
|
||||
surface_pipe = MAT_PIPE_FORWARD;
|
||||
prepass_pipe = has_motion ? MAT_PIPE_FORWARD_PREPASS_VELOCITY : MAT_PIPE_FORWARD_PREPASS;
|
||||
|
||||
MaterialKey material_key(blender_mat, geometry_type, surface_pipe);
|
||||
|
||||
Material &mat = material_map_.lookup_or_add_cb(material_key, [&]() {
|
||||
|
|
|
@ -278,4 +278,184 @@ void ForwardPipeline::render(View &view,
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Deferred Pipeline
|
||||
*
|
||||
* Closure data are written to intermediate buffer allowing screen space processing.
|
||||
* \{ */
|
||||
|
||||
void DeferredLayer::sync()
|
||||
{
|
||||
DRWState state_depth_only = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
|
||||
DRWState state_depth_color = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS |
|
||||
DRW_STATE_WRITE_COLOR;
|
||||
{
|
||||
prepass_ps_.init();
|
||||
prepass_ps_.clear_stencil(0x00u);
|
||||
|
||||
{
|
||||
/* Common resources. */
|
||||
|
||||
/* Textures. */
|
||||
prepass_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
|
||||
/* Uniform Buf. */
|
||||
prepass_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get());
|
||||
|
||||
inst_.velocity.bind_resources(&prepass_ps_);
|
||||
inst_.sampling.bind_resources(&prepass_ps_);
|
||||
}
|
||||
|
||||
prepass_double_sided_static_ps_ = &prepass_ps_.sub("DoubleSided.Static");
|
||||
prepass_double_sided_static_ps_->state_set(state_depth_only);
|
||||
|
||||
prepass_single_sided_static_ps_ = &prepass_ps_.sub("SingleSided.Static");
|
||||
prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK);
|
||||
|
||||
prepass_double_sided_moving_ps_ = &prepass_ps_.sub("DoubleSided.Moving");
|
||||
prepass_double_sided_moving_ps_->state_set(state_depth_color);
|
||||
|
||||
prepass_single_sided_moving_ps_ = &prepass_ps_.sub("SingleSided.Moving");
|
||||
prepass_single_sided_moving_ps_->state_set(state_depth_color | DRW_STATE_CULL_BACK);
|
||||
}
|
||||
{
|
||||
gbuffer_ps_.init();
|
||||
|
||||
{
|
||||
/* Common resources. */
|
||||
|
||||
/* G-buffer. */
|
||||
gbuffer_ps_.bind_image(GBUF_CLOSURE_SLOT, &inst_.gbuffer.closure_tx);
|
||||
gbuffer_ps_.bind_image(GBUF_COLOR_SLOT, &inst_.gbuffer.color_tx);
|
||||
|
||||
/* RenderPasses. */
|
||||
gbuffer_ps_.bind_image(RBUFS_NORMAL_SLOT, &inst_.render_buffers.normal_tx);
|
||||
/* TODO(fclem): Pack all render pass into the same texture. */
|
||||
// gbuffer_ps_.bind_image(RBUFS_DIFF_COLOR_SLOT, &inst_.render_buffers.diffuse_color_tx);
|
||||
gbuffer_ps_.bind_image(RBUFS_SPEC_COLOR_SLOT, &inst_.render_buffers.specular_color_tx);
|
||||
gbuffer_ps_.bind_image(RBUFS_EMISSION_SLOT, &inst_.render_buffers.emission_tx);
|
||||
/* AOVs. */
|
||||
gbuffer_ps_.bind_image(RBUFS_AOV_COLOR_SLOT, &inst_.render_buffers.aov_color_tx);
|
||||
gbuffer_ps_.bind_image(RBUFS_AOV_VALUE_SLOT, &inst_.render_buffers.aov_value_tx);
|
||||
/* Cryptomatte. */
|
||||
gbuffer_ps_.bind_image(RBUFS_CRYPTOMATTE_SLOT, &inst_.render_buffers.cryptomatte_tx);
|
||||
/* Storage Buf. */
|
||||
gbuffer_ps_.bind_ssbo(RBUFS_AOV_BUF_SLOT, &inst_.film.aovs_info);
|
||||
/* Textures. */
|
||||
gbuffer_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
|
||||
/* Uniform Buf. */
|
||||
gbuffer_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get());
|
||||
|
||||
inst_.sampling.bind_resources(&gbuffer_ps_);
|
||||
inst_.cryptomatte.bind_resources(&gbuffer_ps_);
|
||||
}
|
||||
|
||||
gbuffer_single_sided_ps_ = &gbuffer_ps_.sub("SingleSided");
|
||||
gbuffer_single_sided_ps_->state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL |
|
||||
DRW_STATE_CULL_BACK);
|
||||
|
||||
gbuffer_double_sided_ps_ = &gbuffer_ps_.sub("DoubleSided");
|
||||
gbuffer_double_sided_ps_->state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
|
||||
}
|
||||
}
|
||||
|
||||
PassMain::Sub *DeferredLayer::prepass_add(::Material *blender_mat,
|
||||
GPUMaterial *gpumat,
|
||||
bool has_motion)
|
||||
{
|
||||
closure_bits_ |= shader_closure_bits_from_flag(gpumat);
|
||||
|
||||
PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
|
||||
(has_motion ? prepass_single_sided_moving_ps_ :
|
||||
prepass_single_sided_static_ps_) :
|
||||
(has_motion ? prepass_double_sided_moving_ps_ :
|
||||
prepass_double_sided_static_ps_);
|
||||
return &pass->sub(GPU_material_get_name(gpumat));
|
||||
}
|
||||
|
||||
PassMain::Sub *DeferredLayer::material_add(::Material *blender_mat, GPUMaterial *gpumat)
|
||||
{
|
||||
PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
|
||||
gbuffer_single_sided_ps_ :
|
||||
gbuffer_double_sided_ps_;
|
||||
return &pass->sub(GPU_material_get_name(gpumat));
|
||||
}
|
||||
|
||||
void DeferredLayer::render(View &view,
|
||||
Framebuffer &prepass_fb,
|
||||
Framebuffer &combined_fb,
|
||||
int2 extent)
|
||||
{
|
||||
inst_.gbuffer.acquire(extent, closure_bits_);
|
||||
|
||||
GPU_framebuffer_bind(prepass_fb);
|
||||
inst_.manager->submit(prepass_ps_, view);
|
||||
|
||||
inst_.hiz_buffer.set_dirty();
|
||||
|
||||
GPU_framebuffer_bind(combined_fb);
|
||||
inst_.manager->submit(gbuffer_ps_, view);
|
||||
|
||||
inst_.shadows.set_view(view);
|
||||
inst_.manager->submit(inst_.pipelines.deferred.eval_ps_);
|
||||
|
||||
/* TODO evaluate lights. */
|
||||
|
||||
inst_.gbuffer.release();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Deferred Pipeline
|
||||
*
|
||||
* Closure data are written to intermediate buffer allowing screen space processing.
|
||||
* \{ */
|
||||
|
||||
void DeferredPipeline::sync()
|
||||
{
|
||||
opaque_layer_.sync();
|
||||
refraction_layer_.sync();
|
||||
|
||||
eval_ps_.init();
|
||||
/* TODO Lighting. */
|
||||
}
|
||||
|
||||
PassMain::Sub *DeferredPipeline::prepass_add(::Material *blender_mat,
|
||||
GPUMaterial *gpumat,
|
||||
bool has_motion)
|
||||
{
|
||||
if (blender_mat->blend_flag & MA_BL_SS_REFRACTION) {
|
||||
return refraction_layer_.prepass_add(blender_mat, gpumat, has_motion);
|
||||
}
|
||||
else {
|
||||
return opaque_layer_.prepass_add(blender_mat, gpumat, has_motion);
|
||||
}
|
||||
}
|
||||
|
||||
PassMain::Sub *DeferredPipeline::material_add(::Material *blender_mat, GPUMaterial *gpumat)
|
||||
{
|
||||
if (blender_mat->blend_flag & MA_BL_SS_REFRACTION) {
|
||||
return refraction_layer_.material_add(blender_mat, gpumat);
|
||||
}
|
||||
else {
|
||||
return opaque_layer_.material_add(blender_mat, gpumat);
|
||||
}
|
||||
}
|
||||
|
||||
void DeferredPipeline::render(View &view,
|
||||
Framebuffer &prepass_fb,
|
||||
Framebuffer &combined_fb,
|
||||
int2 extent)
|
||||
{
|
||||
DRW_stats_group_start("Deferred.Opaque");
|
||||
opaque_layer_.render(view, prepass_fb, combined_fb, extent);
|
||||
DRW_stats_group_end();
|
||||
|
||||
DRW_stats_group_start("Deferred.Refract");
|
||||
refraction_layer_.render(view, prepass_fb, combined_fb, extent);
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
|
|
|
@ -113,6 +113,65 @@ class ForwardPipeline {
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Deferred lighting.
|
||||
* \{ */
|
||||
|
||||
class DeferredLayer {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
PassMain prepass_ps_ = {"Prepass"};
|
||||
PassMain::Sub *prepass_single_sided_static_ps_ = nullptr;
|
||||
PassMain::Sub *prepass_single_sided_moving_ps_ = nullptr;
|
||||
PassMain::Sub *prepass_double_sided_static_ps_ = nullptr;
|
||||
PassMain::Sub *prepass_double_sided_moving_ps_ = nullptr;
|
||||
|
||||
PassMain gbuffer_ps_ = {"Shading"};
|
||||
PassMain::Sub *gbuffer_single_sided_ps_ = nullptr;
|
||||
PassMain::Sub *gbuffer_double_sided_ps_ = nullptr;
|
||||
|
||||
/* Closures bits from the materials in this pass. */
|
||||
eClosureBits closure_bits_;
|
||||
|
||||
public:
|
||||
DeferredLayer(Instance &inst) : inst_(inst){};
|
||||
|
||||
void sync();
|
||||
|
||||
PassMain::Sub *prepass_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion);
|
||||
PassMain::Sub *material_add(::Material *blender_mat, GPUMaterial *gpumat);
|
||||
|
||||
void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent);
|
||||
};
|
||||
|
||||
class DeferredPipeline {
|
||||
public:
|
||||
PassSimple eval_ps_ = {"EvalLighting"};
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/* Gbuffer filling passes. We could have an arbitrary number of them but for now we just have
|
||||
* a hardcoded number of them. */
|
||||
DeferredLayer opaque_layer_;
|
||||
DeferredLayer refraction_layer_;
|
||||
DeferredLayer volumetric_layer_;
|
||||
|
||||
public:
|
||||
DeferredPipeline(Instance &inst)
|
||||
: inst_(inst), opaque_layer_(inst), refraction_layer_(inst), volumetric_layer_(inst){};
|
||||
|
||||
void sync();
|
||||
|
||||
PassMain::Sub *prepass_add(::Material *material, GPUMaterial *gpumat, bool has_motion);
|
||||
PassMain::Sub *material_add(::Material *material, GPUMaterial *gpumat);
|
||||
|
||||
void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utility texture
|
||||
*
|
||||
|
@ -197,22 +256,20 @@ class UtilityTexture : public Texture {
|
|||
class PipelineModule {
|
||||
public:
|
||||
WorldPipeline world;
|
||||
// DeferredPipeline deferred;
|
||||
DeferredPipeline deferred;
|
||||
ForwardPipeline forward;
|
||||
ShadowPipeline shadow;
|
||||
// VelocityPipeline velocity;
|
||||
|
||||
UtilityTexture utility_tx;
|
||||
|
||||
public:
|
||||
PipelineModule(Instance &inst) : world(inst), forward(inst), shadow(inst){};
|
||||
PipelineModule(Instance &inst) : world(inst), deferred(inst), forward(inst), shadow(inst){};
|
||||
|
||||
void sync()
|
||||
{
|
||||
// deferred.sync();
|
||||
deferred.sync();
|
||||
forward.sync();
|
||||
shadow.sync();
|
||||
// velocity.sync();
|
||||
}
|
||||
|
||||
PassMain::Sub *material_add(Object *ob,
|
||||
|
@ -222,7 +279,7 @@ class PipelineModule {
|
|||
{
|
||||
switch (pipeline_type) {
|
||||
case MAT_PIPE_DEFERRED_PREPASS:
|
||||
// return deferred.prepass_add(blender_mat, gpumat, false);
|
||||
return deferred.prepass_add(blender_mat, gpumat, false);
|
||||
case MAT_PIPE_FORWARD_PREPASS:
|
||||
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
|
||||
return forward.prepass_transparent_add(ob, blender_mat, gpumat);
|
||||
|
@ -230,7 +287,7 @@ class PipelineModule {
|
|||
return forward.prepass_opaque_add(blender_mat, gpumat, false);
|
||||
|
||||
case MAT_PIPE_DEFERRED_PREPASS_VELOCITY:
|
||||
// return deferred.prepass_add(blender_mat, gpumat, true);
|
||||
return deferred.prepass_add(blender_mat, gpumat, true);
|
||||
case MAT_PIPE_FORWARD_PREPASS_VELOCITY:
|
||||
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
|
||||
return forward.prepass_transparent_add(ob, blender_mat, gpumat);
|
||||
|
@ -238,7 +295,7 @@ class PipelineModule {
|
|||
return forward.prepass_opaque_add(blender_mat, gpumat, true);
|
||||
|
||||
case MAT_PIPE_DEFERRED:
|
||||
// return deferred.material_add(blender_mat, gpumat);
|
||||
return deferred.material_add(blender_mat, gpumat);
|
||||
case MAT_PIPE_FORWARD:
|
||||
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
|
||||
return forward.material_transparent_add(ob, blender_mat, gpumat);
|
||||
|
|
|
@ -123,8 +123,7 @@ void ShadingView::render()
|
|||
/* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */
|
||||
inst_.lights.set_view(render_view_new_, extent_);
|
||||
|
||||
// inst_.pipelines.deferred.render(
|
||||
// render_view_, rt_buffer_opaque_, rt_buffer_refract_, depth_tx_, combined_tx_);
|
||||
inst_.pipelines.deferred.render(render_view_new_, prepass_fb_, combined_fb_, extent_);
|
||||
|
||||
// inst_.lightprobes.draw_cache_display();
|
||||
|
||||
|
|
|
@ -1,58 +1,75 @@
|
|||
|
||||
/**
|
||||
* G-buffer: Packing and upacking for the of the G-buffer data.
|
||||
* G-buffer: Packing and upacking of G-buffer data.
|
||||
*
|
||||
* See `GPU_SHADER_CREATE_INFO(eevee_surf_deferred)` for a breakdown of the G-buffer layout.
|
||||
* See #GBuffer for a breakdown of the G-buffer layout.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
|
||||
|
||||
vec2 gbuffer_normal_pack(vec3 N)
|
||||
{
|
||||
N /= length_manhattan(N);
|
||||
N.xy = (N.z >= 0.0) ? N.xy : ((1.0 - abs(N.yx)) * sign(N.xy));
|
||||
N.xy = N.xy * 0.5 + 0.5;
|
||||
return N.xy;
|
||||
}
|
||||
|
||||
vec3 gbuffer_normal_unpack(vec2 N_packed)
|
||||
{
|
||||
return vec3(N_packed.xy, sqrt(1.0 - sqr(N_packed.x) - sqr(N_packed.y)));
|
||||
N_packed = N_packed * 2.0 - 1.0;
|
||||
vec3 N = vec3(N_packed.x, N_packed.y, 1.0 - abs(N_packed.x) - abs(N_packed.y));
|
||||
float t = clamp(-N.z, 0.0, 1.0);
|
||||
N.x += (N.x >= 0.0) ? -t : t;
|
||||
N.y += (N.y >= 0.0) ? -t : t;
|
||||
return normalize(N);
|
||||
}
|
||||
|
||||
float gbuffer_ior_pack(float ior)
|
||||
{
|
||||
return ior;
|
||||
return (ior > 1.0) ? (1.0 - 0.5 / ior) : (0.5 * ior);
|
||||
}
|
||||
|
||||
float gbuffer_ior_unpack(float ior_packed)
|
||||
{
|
||||
return ior_packed;
|
||||
return (ior_packed > 0.5) ? (-1.0 / (ior_packed * 2.0 + 2.0)) : (2.0 * ior_packed);
|
||||
}
|
||||
|
||||
float gbuffer_thickness_pack(float thickness)
|
||||
{
|
||||
/* TODO */
|
||||
return thickness;
|
||||
}
|
||||
|
||||
float gbuffer_thickness_unpack(float thickness_packed)
|
||||
{
|
||||
/* TODO */
|
||||
return thickness_packed;
|
||||
}
|
||||
|
||||
vec3 gbuffer_sss_radii_pack(vec3 sss_radii)
|
||||
{
|
||||
return sss_radii;
|
||||
/* TODO(fclem): Something better. */
|
||||
return vec3(
|
||||
gbuffer_ior_pack(sss_radii.x), gbuffer_ior_pack(sss_radii.y), gbuffer_ior_pack(sss_radii.z));
|
||||
}
|
||||
|
||||
vec3 gbuffer_sss_radii_unpack(vec3 sss_radii_packed)
|
||||
{
|
||||
return sss_radii_packed;
|
||||
/* TODO(fclem): Something better. */
|
||||
return vec3(gbuffer_ior_unpack(sss_radii_packed.x),
|
||||
gbuffer_ior_unpack(sss_radii_packed.y),
|
||||
gbuffer_ior_unpack(sss_radii_packed.z));
|
||||
}
|
||||
|
||||
vec4 gbuffer_color_pack(vec3 color)
|
||||
{
|
||||
/* TODO(fclem): 2 exponent inside 2bit Alpha. */
|
||||
return vec4(color, 1.0);
|
||||
}
|
||||
|
||||
vec3 gbuffer_color_unpack(vec4 color_packed)
|
||||
{
|
||||
/* TODO(fclem): 2 exponent inside 2bit Alpha. */
|
||||
return color_packed.rgb;
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
* Some render-pass are written during this pass.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
|
||||
|
||||
vec4 closure_to_rgba(Closure cl)
|
||||
{
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
/* Float Math */
|
||||
|
||||
/* WORKAROUND: To be removed once we port all code to use gpu_shader_math_base_lib.glsl. */
|
||||
#ifndef GPU_SHADER_MATH_BASE_LIB_GLSL
|
||||
|
||||
float safe_divide(float a, float b)
|
||||
{
|
||||
return (b != 0.0) ? a / b : 0.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* fmod function compatible with OSL (copy from OSL/dual.h) */
|
||||
float compatible_fmod(float a, float b)
|
||||
{
|
||||
if (b != 0.0f) {
|
||||
if (b != 0.0) {
|
||||
int N = int(a / b);
|
||||
return a - N * b;
|
||||
}
|
||||
return 0.0f;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float compatible_pow(float x, float y)
|
||||
|
@ -59,11 +64,16 @@ vec3 wrap(vec3 a, vec3 b, vec3 c)
|
|||
return vec3(wrap(a.x, b.x, c.x), wrap(a.y, b.y, c.y), wrap(a.z, b.z, c.z));
|
||||
}
|
||||
|
||||
/* WORKAROUND: To be removed once we port all code to use gpu_shader_math_base_lib.glsl. */
|
||||
#ifndef GPU_SHADER_MATH_BASE_LIB_GLSL
|
||||
|
||||
float hypot(float x, float y)
|
||||
{
|
||||
return sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int floor_to_int(float x)
|
||||
{
|
||||
return int(floor(x));
|
||||
|
@ -76,6 +86,9 @@ int quick_floor(float x)
|
|||
|
||||
/* Vector Math */
|
||||
|
||||
/* WORKAROUND: To be removed once we port all code to use gpu_shader_math_base_lib.glsl. */
|
||||
#ifndef GPU_SHADER_MATH_BASE_LIB_GLSL
|
||||
|
||||
vec2 safe_divide(vec2 a, vec2 b)
|
||||
{
|
||||
return vec2(safe_divide(a.x, b.x), safe_divide(a.y, b.y));
|
||||
|
@ -107,6 +120,8 @@ vec4 safe_divide(vec4 a, float b)
|
|||
return (b != 0.0) ? a / b : vec4(0.0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
vec3 compatible_fmod(vec3 a, vec3 b)
|
||||
{
|
||||
return vec3(compatible_fmod(a.x, b.x), compatible_fmod(a.y, b.y), compatible_fmod(a.z, b.z));
|
||||
|
|
Loading…
Reference in New Issue