Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/intern
diff options
context:
space:
mode:
Diffstat (limited to 'intern')
-rw-r--r--intern/cycles/blender/addon/properties.py7
-rw-r--r--intern/cycles/blender/addon/ui.py24
-rw-r--r--intern/cycles/blender/blender_object.cpp6
-rw-r--r--intern/cycles/kernel/bvh/bvh.h8
-rw-r--r--intern/cycles/kernel/closure/bsdf.h32
-rw-r--r--intern/cycles/kernel/kernel_camera.h16
-rw-r--r--intern/cycles/kernel/kernel_light.h8
-rw-r--r--intern/cycles/kernel/kernel_passes.h2
-rw-r--r--intern/cycles/kernel/kernel_types.h3
-rw-r--r--intern/cycles/render/light.cpp22
-rw-r--r--intern/cycles/render/light.h5
-rw-r--r--intern/cycles/render/object.cpp2
-rw-r--r--intern/cycles/render/object.h1
-rw-r--r--intern/cycles/util/util_guarded_allocator.h1
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.cpp3
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.cpp9
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.h3
-rw-r--r--intern/opencolorio/ocio_impl_glsl.cc17
18 files changed, 144 insertions, 25 deletions
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index c91e210bbd8..da18ac7c693 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -1205,6 +1205,13 @@ class CyclesObjectSettings(bpy.types.PropertyGroup):
default=1.0,
)
+ shadow_terminator_offset: FloatProperty(
+ name="Shadow Terminator Offset",
+ description="Push the shadow terminator towards the light to hide artifacts on low poly geometry",
+ min=0.0, max=1.0,
+ default=0.0,
+ )
+
is_shadow_catcher: BoolProperty(
name="Shadow Catcher",
description="Only render shadows on this object, for compositing renders into real footage",
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index a15daee706f..78a44881743 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -1209,6 +1209,27 @@ def has_geometry_visibility(ob):
return ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT'}) or
(ob.instance_type == 'COLLECTION' and ob.instance_collection))
+class CYCLES_OBJECT_PT_shading(CyclesButtonsPanel, Panel):
+ bl_label = "Shading"
+ bl_context = "object"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ return CyclesButtonsPanel.poll(context) and (context.object)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+ layout = self.layout
+ ob = context.object
+ cob = ob.cycles
+
+ if has_geometry_visibility(ob):
+ col = flow.column()
+ col.prop(cob, "shadow_terminator_offset")
class CYCLES_OBJECT_PT_visibility(CyclesButtonsPanel, Panel):
bl_label = "Visibility"
@@ -1367,7 +1388,7 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel):
col.separator()
if light.type in {'POINT', 'SPOT'}:
- col.prop(light, "shadow_soft_size", text="Size")
+ col.prop(light, "shadow_soft_size", text="Radius")
elif light.type == 'SUN':
col.prop(light, "angle")
elif light.type == 'AREA':
@@ -2268,6 +2289,7 @@ classes = (
CYCLES_CAMERA_PT_dof_aperture,
CYCLES_PT_context_material,
CYCLES_OBJECT_PT_motion_blur,
+ CYCLES_OBJECT_PT_shading,
CYCLES_OBJECT_PT_visibility,
CYCLES_OBJECT_PT_visibility_ray_visibility,
CYCLES_OBJECT_PT_visibility_culling,
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index a461982a538..c28586d0f63 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -238,6 +238,12 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
object_updated = true;
}
+ float shadow_terminator_offset = get_float(cobject, "shadow_terminator_offset");
+ if (shadow_terminator_offset != object->shadow_terminator_offset) {
+ object->shadow_terminator_offset = shadow_terminator_offset;
+ object_updated = true;
+ }
+
/* sync the asset name for Cryptomatte */
BL::Object parent = b_ob.parent();
ustring parent_name;
diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h
index b3992c03a9a..9b9df883b62 100644
--- a/intern/cycles/kernel/bvh/bvh.h
+++ b/intern/cycles/kernel/bvh/bvh.h
@@ -300,7 +300,9 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg,
// Is set to zero on miss or if ray is aborted, so can be used as return value
uint p5 = max_hits;
- local_isect->num_hits = 0; // Initialize hit count to zero
+ if (local_isect) {
+ local_isect->num_hits = 0; // Initialize hit count to zero
+ }
optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0,
ray->P,
ray->D,
@@ -323,7 +325,9 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg,
return p5;
# else /* __KERNEL_OPTIX__ */
if (!scene_intersect_valid(ray)) {
- local_isect->num_hits = 0;
+ if (local_isect) {
+ local_isect->num_hits = 0;
+ }
return false;
}
diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h
index dc80e67a891..4cc61e8ee71 100644
--- a/intern/cycles/kernel/closure/bsdf.h
+++ b/intern/cycles/kernel/closure/bsdf.h
@@ -97,6 +97,18 @@ ccl_device_inline float bump_shadowing_term(float3 Ng, float3 N, float3 I)
return -g2 * g + g2 + g;
}
+/* Shadow terminator workaround, taken from Appleseed.
+ * Original code is under the MIT License
+ * Copyright (c) 2019 Francois Beaune, The appleseedhq Organization */
+ccl_device_inline float shift_cos_in(float cos_in, const float frequency_multiplier)
+{
+ cos_in = min(cos_in, 1.0f);
+
+ const float angle = fast_acosf(cos_in);
+ const float val = max(cosf(angle * frequency_multiplier), 0.0f) / cos_in;
+ return val;
+}
+
ccl_device_inline int bsdf_sample(KernelGlobals *kg,
ShaderData *sd,
const ShaderClosure *sc,
@@ -444,9 +456,17 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
}
}
}
- else if (label & LABEL_DIFFUSE) {
- if (!isequal_float3(sc->N, sd->N)) {
- *eval *= bump_shadowing_term((label & LABEL_TRANSMIT) ? -sd->N : sd->N, sc->N, *omega_in);
+ else {
+ /* Shadow terminator offset. */
+ const float frequency_multiplier =
+ kernel_tex_fetch(__objects, sd->object).shadow_terminator_offset;
+ if (frequency_multiplier > 1.0f) {
+ *eval *= shift_cos_in(dot(*omega_in, sc->N), frequency_multiplier);
+ }
+ if (label & LABEL_DIFFUSE) {
+ if (!isequal_float3(sc->N, sd->N)) {
+ *eval *= bump_shadowing_term((label & LABEL_TRANSMIT) ? -sd->N : sd->N, sc->N, *omega_in);
+ }
}
}
@@ -561,6 +581,12 @@ ccl_device_inline
eval *= bump_shadowing_term(sd->N, sc->N, omega_in);
}
}
+ /* Shadow terminator offset. */
+ const float frequency_multiplier =
+ kernel_tex_fetch(__objects, sd->object).shadow_terminator_offset;
+ if (frequency_multiplier > 1.0f) {
+ eval *= shift_cos_in(dot(omega_in, sc->N), frequency_multiplier);
+ }
}
else {
switch (sc->type) {
diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h
index 62ce04ba48f..445cf9eb44b 100644
--- a/intern/cycles/kernel/kernel_camera.h
+++ b/intern/cycles/kernel/kernel_camera.h
@@ -441,8 +441,22 @@ ccl_device_inline float camera_distance(KernelGlobals *kg, float3 P)
float3 camD = make_float3(cameratoworld.x.z, cameratoworld.y.z, cameratoworld.z.z);
return fabsf(dot((P - camP), camD));
}
- else
+ else {
return len(P - camP);
+ }
+}
+
+ccl_device_inline float camera_z_depth(KernelGlobals *kg, float3 P)
+{
+ if (kernel_data.cam.type != CAMERA_PANORAMA) {
+ Transform worldtocamera = kernel_data.cam.worldtocamera;
+ return transform_point(&worldtocamera, P).z;
+ }
+ else {
+ Transform cameratoworld = kernel_data.cam.cameratoworld;
+ float3 camP = make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w);
+ return len(P - camP);
+ }
}
ccl_device_inline float3 camera_direction_from_point(KernelGlobals *kg, float3 P)
diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h
index d918abed381..04472212d0c 100644
--- a/intern/cycles/kernel/kernel_light.h
+++ b/intern/cycles/kernel/kernel_light.h
@@ -249,13 +249,13 @@ ccl_device float3 background_map_sample(KernelGlobals *kg, float randu, float ra
float u = (index_u + du) / res_x;
/* compute pdf */
- float denom = cdf_last_u.x * cdf_last_v.x;
float sin_theta = sinf(M_PI_F * v);
+ float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x;
if (sin_theta == 0.0f || denom == 0.0f)
*pdf = 0.0f;
else
- *pdf = (cdf_u.x * cdf_v.x) / (M_2PI_F * M_PI_F * sin_theta * denom);
+ *pdf = (cdf_u.x * cdf_v.x) / denom;
/* compute direction */
return equirectangular_to_direction(u, v);
@@ -284,7 +284,7 @@ ccl_device float background_map_pdf(KernelGlobals *kg, float3 direction)
index_v * cdf_width + res_x);
float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y);
- float denom = cdf_last_u.x * cdf_last_v.x;
+ float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x;
if (denom == 0.0f)
return 0.0f;
@@ -294,7 +294,7 @@ ccl_device float background_map_pdf(KernelGlobals *kg, float3 direction)
index_v * cdf_width + index_u);
float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v);
- return (cdf_u.x * cdf_v.x) / (M_2PI_F * M_PI_F * sin_theta * denom);
+ return (cdf_u.x * cdf_v.x) / denom;
}
ccl_device_inline bool background_portal_data_fetch_and_check_side(
diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h
index 7437e540a1f..753cf4561b2 100644
--- a/intern/cycles/kernel/kernel_passes.h
+++ b/intern/cycles/kernel/kernel_passes.h
@@ -194,7 +194,7 @@ ccl_device_inline void kernel_write_data_passes(KernelGlobals *kg,
average(shader_bsdf_alpha(kg, sd)) >= kernel_data.film.pass_alpha_threshold) {
if (state->sample == 0) {
if (flag & PASSMASK(DEPTH)) {
- float depth = camera_distance(kg, sd->P);
+ float depth = camera_z_depth(kg, sd->P);
kernel_write_pass_float(buffer + kernel_data.film.pass_depth, depth);
}
if (flag & PASSMASK(OBJECT_ID)) {
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 304835a1685..630d00a4e71 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -1480,6 +1480,9 @@ typedef struct KernelObject {
float cryptomatte_object;
float cryptomatte_asset;
+
+ float shadow_terminator_offset;
+ float pad1, pad2, pad3;
} KernelObject;
static_assert_align(KernelObject, 16);
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index cd19b03ac53..cb7474017fa 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -120,6 +120,7 @@ NODE_DEFINE(Light)
SOCKET_VECTOR(dir, "Dir", make_float3(0.0f, 0.0f, 0.0f));
SOCKET_FLOAT(size, "Size", 0.0f);
+ SOCKET_FLOAT(angle, "Angle", 0.0f);
SOCKET_VECTOR(axisu, "Axis U", make_float3(0.0f, 0.0f, 0.0f));
SOCKET_FLOAT(sizeu, "Size U", 1.0f);
@@ -183,6 +184,8 @@ LightManager::LightManager()
need_update = true;
need_update_background = true;
use_light_visibility = false;
+ last_background_enabled = false;
+ last_background_resolution = 0;
}
LightManager::~LightManager()
@@ -202,7 +205,7 @@ bool LightManager::has_background_light(Scene *scene)
return false;
}
-void LightManager::disable_ineffective_light(Scene *scene)
+void LightManager::test_enabled_lights(Scene *scene)
{
/* Make all lights enabled by default, and perform some preliminary checks
* needed for finer-tuning of settings (for example, check whether we've
@@ -215,6 +218,9 @@ void LightManager::disable_ineffective_light(Scene *scene)
has_background |= light->type == LIGHT_BACKGROUND;
}
+ bool background_enabled = false;
+ int background_resolution = 0;
+
if (has_background) {
/* Ignore background light if:
* - If unsupported on a device
@@ -226,9 +232,18 @@ void LightManager::disable_ineffective_light(Scene *scene)
foreach (Light *light, scene->lights) {
if (light->type == LIGHT_BACKGROUND) {
light->is_enabled = !disable_mis;
+ background_enabled = !disable_mis;
+ background_resolution = light->map_resolution;
}
}
}
+
+ if (last_background_enabled != background_enabled ||
+ last_background_resolution != background_resolution) {
+ last_background_enabled = background_enabled;
+ last_background_resolution = background_resolution;
+ need_update_background = true;
+ }
}
bool LightManager::object_usable_as_light(Object *object)
@@ -902,12 +917,13 @@ void LightManager::device_update(Device *device,
VLOG(1) << "Total " << scene->lights.size() << " lights.";
+ /* Detect which lights are enabled, also determins if we need to update the background. */
+ test_enabled_lights(scene);
+
device_free(device, dscene, need_update_background);
use_light_visibility = false;
- disable_ineffective_light(scene);
-
device_update_points(device, dscene, scene);
if (progress.get_cancel())
return;
diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h
index 1a99b2b76ae..d136e8f1a08 100644
--- a/intern/cycles/render/light.h
+++ b/intern/cycles/render/light.h
@@ -112,7 +112,7 @@ class LightManager {
* which doesn't contribute to the scene or which is only used for MIS
* and scene doesn't need MIS.
*/
- void disable_ineffective_light(Scene *scene);
+ void test_enabled_lights(Scene *scene);
void device_update_points(Device *device, DeviceScene *dscene, Scene *scene);
void device_update_distribution(Device *device,
@@ -136,6 +136,9 @@ class LightManager {
vector<IESSlot *> ies_slots;
thread_mutex ies_mutex;
+
+ bool last_background_enabled;
+ int last_background_resolution;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp
index 90a1d90019d..61deef4cd76 100644
--- a/intern/cycles/render/object.cpp
+++ b/intern/cycles/render/object.cpp
@@ -101,6 +101,7 @@ NODE_DEFINE(Object)
SOCKET_POINT(dupli_generated, "Dupli Generated", make_float3(0.0f, 0.0f, 0.0f));
SOCKET_POINT2(dupli_uv, "Dupli UV", make_float2(0.0f, 0.0f));
SOCKET_TRANSFORM_ARRAY(motion, "Motion", array<Transform>());
+ SOCKET_FLOAT(shadow_terminator_offset, "Terminator Offset", 0.0f);
SOCKET_BOOLEAN(is_shadow_catcher, "Shadow Catcher", false);
@@ -534,6 +535,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
uint32_t hash_asset = util_murmur_hash3(ob->asset_name.c_str(), ob->asset_name.length(), 0);
kobject.cryptomatte_object = util_hash_to_float(hash_name);
kobject.cryptomatte_asset = util_hash_to_float(hash_asset);
+ kobject.shadow_terminator_offset = 1.0f / (1.0f - 0.5f * ob->shadow_terminator_offset);
/* Object flag. */
if (ob->use_holdout) {
diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h
index 7c84c2de4fb..ac9b4c331f5 100644
--- a/intern/cycles/render/object.h
+++ b/intern/cycles/render/object.h
@@ -59,6 +59,7 @@ class Object : public Node {
bool hide_on_missing_motion;
bool use_holdout;
bool is_shadow_catcher;
+ float shadow_terminator_offset;
float3 dupli_generated;
float2 dupli_uv;
diff --git a/intern/cycles/util/util_guarded_allocator.h b/intern/cycles/util/util_guarded_allocator.h
index 2d09326d2ca..f78cc5f5da9 100644
--- a/intern/cycles/util/util_guarded_allocator.h
+++ b/intern/cycles/util/util_guarded_allocator.h
@@ -18,6 +18,7 @@
#define __UTIL_GUARDED_ALLOCATOR_H__
#include <cstddef>
+#include <cstdlib>
#include <memory>
#ifdef WITH_BLENDER_GUARDEDALLOC
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 633451feb85..31110694ea6 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -1527,7 +1527,7 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool exclusive,
- const bool /*is_dialog*/,
+ const bool is_dialog,
const GHOST_IWindow *parentWindow)
{
GHOST_WindowWayland *window = new GHOST_WindowWayland(
@@ -1540,6 +1540,7 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
state,
parentWindow,
type,
+ is_dialog,
((glSettings.flags & GHOST_glStereoVisual) != 0),
exclusive);
diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp
index 0ea6f5f8ecb..ef02db7abc3 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.cpp
+++ b/intern/ghost/intern/GHOST_WindowWayland.cpp
@@ -39,6 +39,7 @@ struct window_t {
bool is_maximised;
bool is_fullscreen;
bool is_active;
+ bool is_dialog;
int32_t width, height;
};
@@ -144,6 +145,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
GHOST_TWindowState state,
const GHOST_IWindow *parentWindow,
GHOST_TDrawingContextType type,
+ const bool is_dialog,
const bool stereoVisual,
const bool exclusive)
: GHOST_Window(width, height, state, stereoVisual, exclusive),
@@ -155,6 +157,8 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
w->width = int32_t(width);
w->height = int32_t(height);
+ w->is_dialog = is_dialog;
+
/* Window surfaces. */
w->surface = wl_compositor_create_surface(m_system->compositor());
w->egl_window = wl_egl_window_create(w->surface, int(width), int(height));
@@ -376,6 +380,11 @@ GHOST_TSuccess GHOST_WindowWayland::endFullScreen() const
return GHOST_kSuccess;
}
+bool GHOST_WindowWayland::isDialog() const
+{
+ return w->is_dialog;
+}
+
/**
* \param type The type of rendering context create.
* \return Indication of success.
diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h
index 39c35f77d7d..23e55fcd6e4 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.h
+++ b/intern/ghost/intern/GHOST_WindowWayland.h
@@ -42,6 +42,7 @@ class GHOST_WindowWayland : public GHOST_Window {
GHOST_TWindowState state,
const GHOST_IWindow *parentWindow,
GHOST_TDrawingContextType type,
+ const bool is_dialog,
const bool stereoVisual,
const bool exclusive);
@@ -106,6 +107,8 @@ class GHOST_WindowWayland : public GHOST_Window {
GHOST_TSuccess endFullScreen() const override;
+ bool isDialog() const override;
+
private:
GHOST_SystemWayland *m_system;
struct window_t *w;
diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc
index df6adc8f34b..43416f734c5 100644
--- a/intern/opencolorio/ocio_impl_glsl.cc
+++ b/intern/opencolorio/ocio_impl_glsl.cc
@@ -263,19 +263,20 @@ static void updateGLSLShader(OCIO_GLSLShader *shader,
shader->curve_mapping_loc = glGetUniformLocation(shader->program, "curve_mapping");
glUseProgram(shader->program);
- /* Set texture bind point uniform once. This is saved by the shader. */
- glUniform1i(glGetUniformLocation(shader->program, "image_texture"), 0);
- glUniform1i(glGetUniformLocation(shader->program, "lut3d_texture"), 2);
- glUniform1i(glGetUniformLocation(shader->program, "lut3d_display_texture"), 3);
- glUniform1i(glGetUniformLocation(shader->program, "curve_mapping_texture"), 4);
+
+ /* TODO(fclem) Remove this. Make caller always assume viewport space and
+ * specify texco via vertex attribs. */
+ shader->interface = GPU_shaderinterface_create(shader->program);
/* Set UBO binding location. */
GLuint index = glGetUniformBlockIndex(shader->program, "OCIO_GLSLCurveMappingParameters");
glUniformBlockBinding(shader->program, index, UBO_BIND_LOC);
- /* TODO(fclem) Remove this. Make caller always assume viewport space and
- * specify texco via vertex attribs. */
- shader->interface = GPU_shaderinterface_create(shader->program);
+ /* Set texture bind point uniform once. This is saved by the shader. */
+ glUniform1i(glGetUniformLocation(shader->program, "image_texture"), 0);
+ glUniform1i(glGetUniformLocation(shader->program, "lut3d_texture"), 2);
+ glUniform1i(glGetUniformLocation(shader->program, "lut3d_display_texture"), 3);
+ glUniform1i(glGetUniformLocation(shader->program, "curve_mapping_texture"), 4);
}
shader->cacheId = cache_id;