This is mostly to avoid re-compilation when using undo/redo operators. This also has the benefit to reuse the same GPUShader for multiple materials using the same nodetree configuration. The cache stores GPUPasses that already contains the shader code and a hash to test for matches. We use refcounts to know when a GPUPass is not used anymore. I had to move the GPUInput list from GPUPass to GPUMaterial because it's containing references to the material nodetree and cannot be reused. A garbage collection is hardcoded to run every 60 seconds to free every unused GPUPass.
2960 lines
94 KiB
C
2960 lines
94 KiB
C
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* 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.
|
|
*
|
|
* The Original Code is Copyright (C) 2006 Blender Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): Brecht Van Lommel.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/gpu/intern/gpu_material.c
|
|
* \ingroup gpu
|
|
*
|
|
* Manages materials, lights and textures.
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_lamp_types.h"
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_world_types.h"
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_rand.h"
|
|
|
|
#include "BKE_anim.h"
|
|
#include "BKE_colorband.h"
|
|
#include "BKE_colortools.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_image.h"
|
|
#include "BKE_layer.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_node.h"
|
|
#include "BKE_scene.h"
|
|
#include "BKE_group.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
#include "GPU_extensions.h"
|
|
#include "GPU_framebuffer.h"
|
|
#include "GPU_lamp.h"
|
|
#include "GPU_material.h"
|
|
#include "GPU_shader.h"
|
|
#include "GPU_texture.h"
|
|
#include "GPU_uniformbuffer.h"
|
|
|
|
#include "DRW_engine.h"
|
|
|
|
#include "gpu_codegen.h"
|
|
#include "gpu_lamp_private.h"
|
|
|
|
#ifdef WITH_OPENSUBDIV
|
|
# include "BKE_DerivedMesh.h"
|
|
#endif
|
|
|
|
/* Structs */
|
|
|
|
typedef enum DynMatProperty {
|
|
DYN_LAMP_CO = 1,
|
|
DYN_LAMP_VEC = 2,
|
|
DYN_LAMP_IMAT = 4,
|
|
DYN_LAMP_PERSMAT = 8,
|
|
} DynMatProperty;
|
|
|
|
static struct GPUWorld {
|
|
float mistenabled;
|
|
float mistype;
|
|
float miststart;
|
|
float mistdistance;
|
|
float mistintensity;
|
|
float mistcol[4];
|
|
float horicol[3];
|
|
float ambcol[4];
|
|
float zencol[3];
|
|
} GPUWorld;
|
|
|
|
struct GPUMaterial {
|
|
Scene *scene; /* DEPRECATED was only usefull for lamps */
|
|
Material *ma;
|
|
|
|
/* material for mesh surface, worlds or something else.
|
|
* some code generation is done differently depending on the use case */
|
|
int type; /* DEPRECATED */
|
|
GPUMaterialStatus status;
|
|
|
|
const void *engine_type; /* attached engine type */
|
|
int options; /* to identify shader variations (shadow, probe, world background...) */
|
|
|
|
/* for creating the material */
|
|
ListBase nodes;
|
|
GPUNodeLink *outlink;
|
|
|
|
/* for binding the material */
|
|
GPUPass *pass;
|
|
ListBase inputs; /* GPUInput */
|
|
GPUVertexAttribs attribs;
|
|
int builtins;
|
|
int alpha, obcolalpha;
|
|
int dynproperty;
|
|
|
|
/* for passing uniforms */
|
|
int viewmatloc, invviewmatloc;
|
|
int obmatloc, invobmatloc;
|
|
int localtoviewmatloc, invlocaltoviewmatloc;
|
|
int obcolloc, obautobumpscaleloc;
|
|
int cameratexcofacloc;
|
|
|
|
int partscalarpropsloc;
|
|
int partcoloc;
|
|
int partvel;
|
|
int partangvel;
|
|
|
|
int objectinfoloc;
|
|
|
|
ListBase lamps;
|
|
bool bound;
|
|
|
|
bool is_opensubdiv;
|
|
|
|
/* XXX: Should be in Material. But it depends on the output node
|
|
* used and since the output selection is difference for GPUMaterial...
|
|
*/
|
|
int domain;
|
|
|
|
/* Used by 2.8 pipeline */
|
|
GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */
|
|
|
|
/* Eevee SSS */
|
|
GPUUniformBuffer *sss_profile; /* UBO containing SSS profile. */
|
|
GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */
|
|
float *sss_radii; /* UBO containing SSS profile. */
|
|
int sss_samples;
|
|
short int *sss_falloff;
|
|
float *sss_sharpness;
|
|
bool sss_dirty;
|
|
};
|
|
|
|
enum {
|
|
GPU_DOMAIN_SURFACE = (1 << 0),
|
|
GPU_DOMAIN_VOLUME = (1 << 1),
|
|
GPU_DOMAIN_SSS = (1 << 2)
|
|
};
|
|
|
|
/* Forward declaration so shade_light_textures() can use this, while still keeping the code somewhat organized */
|
|
static void texture_rgb_blend(
|
|
GPUMaterial *mat, GPUNodeLink *tex, GPUNodeLink *out, GPUNodeLink *fact, GPUNodeLink *facg,
|
|
int blendtype, GPUNodeLink **in);
|
|
|
|
/* Functions */
|
|
|
|
static GPUMaterial *GPU_material_construct_begin(Material *ma)
|
|
{
|
|
GPUMaterial *material = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial");
|
|
|
|
material->ma = ma;
|
|
|
|
return material;
|
|
}
|
|
|
|
static void gpu_material_set_attrib_id(GPUMaterial *material)
|
|
{
|
|
GPUVertexAttribs *attribs = &material->attribs;
|
|
GPUPass *pass = material->pass;
|
|
if (!pass) {
|
|
attribs->totlayer = 0;
|
|
return;
|
|
}
|
|
|
|
GPUShader *shader = GPU_pass_shader(pass);
|
|
if (!shader) {
|
|
attribs->totlayer = 0;
|
|
return;
|
|
}
|
|
|
|
/* convert from attribute number to the actual id assigned by opengl,
|
|
* in case the attrib does not get a valid index back, it was probably
|
|
* removed by the glsl compiler by dead code elimination */
|
|
|
|
int b = 0;
|
|
for (int a = 0; a < attribs->totlayer; a++) {
|
|
char name[32];
|
|
BLI_snprintf(name, sizeof(name), "att%d", attribs->layer[a].attribid);
|
|
attribs->layer[a].glindex = GPU_shader_get_attribute(shader, name);
|
|
|
|
BLI_snprintf(name, sizeof(name), "att%d_info", attribs->layer[a].attribid);
|
|
attribs->layer[a].glinfoindoex = GPU_shader_get_uniform(shader, name);
|
|
|
|
if (attribs->layer[a].glindex >= 0) {
|
|
attribs->layer[b] = attribs->layer[a];
|
|
b++;
|
|
}
|
|
}
|
|
|
|
attribs->totlayer = b;
|
|
}
|
|
|
|
static int gpu_material_construct_end(GPUMaterial *material, const char *passname)
|
|
{
|
|
if (material->outlink) {
|
|
GPUNodeLink *outlink = material->outlink;
|
|
material->pass = GPU_generate_pass(&material->nodes, &material->inputs, outlink,
|
|
&material->attribs, &material->builtins, material->type,
|
|
passname,
|
|
material->is_opensubdiv,
|
|
GPU_material_use_new_shading_nodes(material));
|
|
|
|
material->status = (material->pass) ? GPU_MAT_SUCCESS : GPU_MAT_FAILED;
|
|
|
|
if (!material->pass)
|
|
return 0;
|
|
|
|
gpu_material_set_attrib_id(material);
|
|
|
|
GPUShader *shader = GPU_pass_shader(material->pass);
|
|
|
|
if (material->builtins & GPU_VIEW_MATRIX)
|
|
material->viewmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_VIEW_MATRIX));
|
|
if (material->builtins & GPU_INVERSE_VIEW_MATRIX)
|
|
material->invviewmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_INVERSE_VIEW_MATRIX));
|
|
if (material->builtins & GPU_OBJECT_MATRIX)
|
|
material->obmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_OBJECT_MATRIX));
|
|
if (material->builtins & GPU_INVERSE_OBJECT_MATRIX)
|
|
material->invobmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_INVERSE_OBJECT_MATRIX));
|
|
if (material->builtins & GPU_LOC_TO_VIEW_MATRIX)
|
|
material->localtoviewmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_LOC_TO_VIEW_MATRIX));
|
|
if (material->builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX)
|
|
material->invlocaltoviewmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_INVERSE_LOC_TO_VIEW_MATRIX));
|
|
if (material->builtins & GPU_OBCOLOR)
|
|
material->obcolloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_OBCOLOR));
|
|
if (material->builtins & GPU_AUTO_BUMPSCALE)
|
|
material->obautobumpscaleloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_AUTO_BUMPSCALE));
|
|
if (material->builtins & GPU_CAMERA_TEXCO_FACTORS)
|
|
material->cameratexcofacloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_CAMERA_TEXCO_FACTORS));
|
|
if (material->builtins & GPU_PARTICLE_SCALAR_PROPS)
|
|
material->partscalarpropsloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_SCALAR_PROPS));
|
|
if (material->builtins & GPU_PARTICLE_LOCATION)
|
|
material->partcoloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_LOCATION));
|
|
if (material->builtins & GPU_PARTICLE_VELOCITY)
|
|
material->partvel = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_VELOCITY));
|
|
if (material->builtins & GPU_PARTICLE_ANG_VELOCITY)
|
|
material->partangvel = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_ANG_VELOCITY));
|
|
if (material->builtins & GPU_OBJECT_INFO)
|
|
material->objectinfoloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_OBJECT_INFO));
|
|
return 1;
|
|
}
|
|
else {
|
|
GPU_pass_free_nodes(&material->nodes);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void GPU_material_free(ListBase *gpumaterial)
|
|
{
|
|
for (LinkData *link = gpumaterial->first; link; link = link->next) {
|
|
GPUMaterial *material = link->data;
|
|
|
|
/* Cancel / wait any pending lazy compilation. */
|
|
DRW_deferred_shader_remove(material);
|
|
|
|
GPU_pass_free_nodes(&material->nodes);
|
|
GPU_inputs_free(&material->inputs);
|
|
|
|
if (material->pass)
|
|
GPU_pass_release(material->pass);
|
|
|
|
if (material->ubo != NULL) {
|
|
GPU_uniformbuffer_free(material->ubo);
|
|
}
|
|
|
|
if (material->sss_tex_profile != NULL) {
|
|
GPU_texture_free(material->sss_tex_profile);
|
|
}
|
|
|
|
if (material->sss_profile != NULL) {
|
|
GPU_uniformbuffer_free(material->sss_profile);
|
|
}
|
|
|
|
BLI_freelistN(&material->lamps);
|
|
|
|
MEM_freeN(material);
|
|
}
|
|
|
|
BLI_freelistN(gpumaterial);
|
|
}
|
|
|
|
void GPU_material_bind(
|
|
GPUMaterial *material, int oblay, int viewlay, double time, int mipmap,
|
|
float viewmat[4][4], float viewinv[4][4], float camerafactors[4])
|
|
{
|
|
if (material->pass) {
|
|
GPUShader *shader = GPU_pass_shader(material->pass);
|
|
|
|
/* handle layer lamps */
|
|
if (material->type == GPU_MATERIAL_TYPE_MESH) {
|
|
for (LinkData *nlink = material->lamps.first; nlink; nlink = nlink->next) {
|
|
GPULamp *lamp = nlink->data;
|
|
|
|
if ((lamp->lay & viewlay) && (!(lamp->mode & LA_LAYER) || (lamp->lay & oblay)) &&
|
|
GPU_lamp_visible(lamp, material->ma))
|
|
{
|
|
lamp->dynenergy = lamp->energy;
|
|
copy_v3_v3(lamp->dyncol, lamp->col);
|
|
}
|
|
else {
|
|
lamp->dynenergy = 0.0f;
|
|
lamp->dyncol[0] = lamp->dyncol[1] = lamp->dyncol[2] = 0.0f;
|
|
}
|
|
|
|
if (material->dynproperty & DYN_LAMP_VEC) {
|
|
copy_v3_v3(lamp->dynvec, lamp->vec);
|
|
normalize_v3(lamp->dynvec);
|
|
negate_v3(lamp->dynvec);
|
|
mul_mat3_m4_v3(viewmat, lamp->dynvec);
|
|
}
|
|
|
|
if (material->dynproperty & DYN_LAMP_CO) {
|
|
copy_v3_v3(lamp->dynco, lamp->co);
|
|
mul_m4_v3(viewmat, lamp->dynco);
|
|
}
|
|
|
|
if (material->dynproperty & DYN_LAMP_IMAT) {
|
|
mul_m4_m4m4(lamp->dynimat, lamp->imat, viewinv);
|
|
}
|
|
|
|
if (material->dynproperty & DYN_LAMP_PERSMAT) {
|
|
/* The lamp matrices are already updated if we're using shadow buffers */
|
|
if (!GPU_lamp_has_shadow_buffer(lamp)) {
|
|
GPU_lamp_update_buffer_mats(lamp);
|
|
}
|
|
mul_m4_m4m4(lamp->dynpersmat, lamp->persmat, viewinv);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* note material must be bound before setting uniforms */
|
|
GPU_pass_bind(material->pass, &material->inputs, time, mipmap);
|
|
|
|
/* handle per material built-ins */
|
|
if (material->builtins & GPU_VIEW_MATRIX) {
|
|
GPU_shader_uniform_vector(shader, material->viewmatloc, 16, 1, (float *)viewmat);
|
|
}
|
|
if (material->builtins & GPU_INVERSE_VIEW_MATRIX) {
|
|
GPU_shader_uniform_vector(shader, material->invviewmatloc, 16, 1, (float *)viewinv);
|
|
}
|
|
if (material->builtins & GPU_CAMERA_TEXCO_FACTORS) {
|
|
if (camerafactors) {
|
|
GPU_shader_uniform_vector(shader, material->cameratexcofacloc, 4, 1, (float *)camerafactors);
|
|
}
|
|
else {
|
|
/* use default, no scaling no offset */
|
|
float borders[4] = {1.0f, 1.0f, 0.0f, 0.0f};
|
|
GPU_shader_uniform_vector(shader, material->cameratexcofacloc, 4, 1, (float *)borders);
|
|
}
|
|
}
|
|
|
|
GPU_pass_update_uniforms(material->pass, &material->inputs);
|
|
|
|
material->bound = 1;
|
|
}
|
|
}
|
|
|
|
GPUBuiltin GPU_get_material_builtins(GPUMaterial *material)
|
|
{
|
|
return material->builtins;
|
|
}
|
|
|
|
void GPU_material_bind_uniforms(
|
|
GPUMaterial *material, float obmat[4][4], float viewmat[4][4], float obcol[4],
|
|
float autobumpscale, GPUParticleInfo *pi, float object_info[3])
|
|
{
|
|
if (material->pass) {
|
|
GPUShader *shader = GPU_pass_shader(material->pass);
|
|
float invmat[4][4], col[4];
|
|
float localtoviewmat[4][4];
|
|
float invlocaltoviewmat[4][4];
|
|
|
|
/* handle per object builtins */
|
|
if (material->builtins & GPU_OBJECT_MATRIX) {
|
|
GPU_shader_uniform_vector(shader, material->obmatloc, 16, 1, (float *)obmat);
|
|
}
|
|
if (material->builtins & GPU_INVERSE_OBJECT_MATRIX) {
|
|
invert_m4_m4(invmat, obmat);
|
|
GPU_shader_uniform_vector(shader, material->invobmatloc, 16, 1, (float *)invmat);
|
|
}
|
|
if (material->builtins & GPU_LOC_TO_VIEW_MATRIX) {
|
|
if (viewmat) {
|
|
mul_m4_m4m4(localtoviewmat, viewmat, obmat);
|
|
GPU_shader_uniform_vector(shader, material->localtoviewmatloc, 16, 1, (float *)localtoviewmat);
|
|
}
|
|
}
|
|
if (material->builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) {
|
|
if (viewmat) {
|
|
mul_m4_m4m4(localtoviewmat, viewmat, obmat);
|
|
invert_m4_m4(invlocaltoviewmat, localtoviewmat);
|
|
GPU_shader_uniform_vector(shader, material->invlocaltoviewmatloc, 16, 1, (float *)invlocaltoviewmat);
|
|
}
|
|
}
|
|
if (material->builtins & GPU_OBCOLOR) {
|
|
copy_v4_v4(col, obcol);
|
|
CLAMP(col[3], 0.0f, 1.0f);
|
|
GPU_shader_uniform_vector(shader, material->obcolloc, 4, 1, col);
|
|
}
|
|
if (material->builtins & GPU_AUTO_BUMPSCALE) {
|
|
GPU_shader_uniform_vector(shader, material->obautobumpscaleloc, 1, 1, &autobumpscale);
|
|
}
|
|
if (material->builtins & GPU_PARTICLE_SCALAR_PROPS) {
|
|
GPU_shader_uniform_vector(shader, material->partscalarpropsloc, 4, 1, pi->scalprops);
|
|
}
|
|
if (material->builtins & GPU_PARTICLE_LOCATION) {
|
|
GPU_shader_uniform_vector(shader, material->partcoloc, 4, 1, pi->location);
|
|
}
|
|
if (material->builtins & GPU_PARTICLE_VELOCITY) {
|
|
GPU_shader_uniform_vector(shader, material->partvel, 3, 1, pi->velocity);
|
|
}
|
|
if (material->builtins & GPU_PARTICLE_ANG_VELOCITY) {
|
|
GPU_shader_uniform_vector(shader, material->partangvel, 3, 1, pi->angular_velocity);
|
|
}
|
|
if (material->builtins & GPU_OBJECT_INFO) {
|
|
GPU_shader_uniform_vector(shader, material->objectinfoloc, 3, 1, object_info);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void GPU_material_unbind(GPUMaterial *material)
|
|
{
|
|
if (material->pass) {
|
|
material->bound = 0;
|
|
GPU_pass_unbind(material->pass, &material->inputs);
|
|
}
|
|
}
|
|
|
|
bool GPU_material_bound(GPUMaterial *material)
|
|
{
|
|
return material->bound;
|
|
}
|
|
|
|
Scene *GPU_material_scene(GPUMaterial *material)
|
|
{
|
|
return material->scene;
|
|
}
|
|
|
|
GPUMatType GPU_Material_get_type(GPUMaterial *material)
|
|
{
|
|
return material->type;
|
|
}
|
|
|
|
GPUPass *GPU_material_get_pass(GPUMaterial *material)
|
|
{
|
|
return material->pass;
|
|
}
|
|
|
|
ListBase *GPU_material_get_inputs(GPUMaterial *material)
|
|
{
|
|
return &material->inputs;
|
|
}
|
|
|
|
GPUUniformBuffer *GPU_material_get_uniform_buffer(GPUMaterial *material)
|
|
{
|
|
return material->ubo;
|
|
}
|
|
|
|
/**
|
|
* Create dynamic UBO from parameters
|
|
* \param ListBase of BLI_genericNodeN(GPUInput)
|
|
*/
|
|
void GPU_material_create_uniform_buffer(GPUMaterial *material, ListBase *inputs)
|
|
{
|
|
material->ubo = GPU_uniformbuffer_dynamic_create(inputs, NULL);
|
|
}
|
|
|
|
void GPU_material_uniform_buffer_tag_dirty(ListBase *gpumaterials)
|
|
{
|
|
for (LinkData *link = gpumaterials->first; link; link = link->next) {
|
|
GPUMaterial *material = link->data;
|
|
if (material->ubo != NULL) {
|
|
GPU_uniformbuffer_tag_dirty(material->ubo);
|
|
}
|
|
if (material->sss_profile != NULL) {
|
|
material->sss_dirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Eevee Subsurface scattering. */
|
|
/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */
|
|
|
|
#define SSS_SAMPLES 65
|
|
#define SSS_EXPONENT 2.0f /* Importance sampling exponent */
|
|
|
|
typedef struct GPUSssKernelData {
|
|
float kernel[SSS_SAMPLES][4];
|
|
float param[3], max_radius;
|
|
int samples;
|
|
} GPUSssKernelData;
|
|
|
|
static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponent)
|
|
{
|
|
float step = 2.0f / (float)(count - 1);
|
|
for (int i = 0; i < count; i++) {
|
|
float o = ((float)i) * step - 1.0f;
|
|
float sign = (o < 0.0f) ? -1.0f : 1.0f;
|
|
float ofs = sign * fabsf(powf(o, exponent));
|
|
kd->kernel[i][3] = ofs;
|
|
}
|
|
}
|
|
|
|
#define GAUSS_TRUNCATE 12.46f
|
|
static float gaussian_profile(float r, float radius)
|
|
{
|
|
const float v = radius * radius * (0.25f * 0.25f);
|
|
const float Rm = sqrtf(v * GAUSS_TRUNCATE);
|
|
|
|
if (r >= Rm) {
|
|
return 0.0f;
|
|
}
|
|
return expf(-r * r / (2.0f * v)) / (2.0f * M_PI * v);
|
|
}
|
|
|
|
#define BURLEY_TRUNCATE 16.0f
|
|
#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE)
|
|
static float burley_profile(float r, float d)
|
|
{
|
|
float exp_r_3_d = expf(-r / (3.0f * d));
|
|
float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d;
|
|
return (exp_r_d + exp_r_3_d) / (4.0f * d);
|
|
}
|
|
|
|
static float cubic_profile(float r, float radius, float sharpness)
|
|
{
|
|
float Rm = radius * (1.0f + sharpness);
|
|
|
|
if (r >= Rm) {
|
|
return 0.0f;
|
|
}
|
|
/* custom variation with extra sharpness, to match the previous code */
|
|
const float y = 1.0f / (1.0f + sharpness);
|
|
float Rmy, ry, ryinv;
|
|
|
|
Rmy = powf(Rm, y);
|
|
ry = powf(r, y);
|
|
ryinv = (r > 0.0f) ? powf(r, y - 1.0f) : 0.0f;
|
|
|
|
const float Rmy5 = (Rmy * Rmy) * (Rmy * Rmy) * Rmy;
|
|
const float f = Rmy - ry;
|
|
const float num = f * (f * f) * (y * ryinv);
|
|
|
|
return (10.0f * num) / (Rmy5 * M_PI);
|
|
}
|
|
|
|
static float eval_profile(float r, short falloff_type, float sharpness, float param)
|
|
{
|
|
r = fabsf(r);
|
|
|
|
if (falloff_type == SHD_SUBSURFACE_BURLEY ||
|
|
falloff_type == SHD_SUBSURFACE_RANDOM_WALK)
|
|
{
|
|
return burley_profile(r, param) / BURLEY_TRUNCATE_CDF;
|
|
}
|
|
else if (falloff_type == SHD_SUBSURFACE_CUBIC) {
|
|
return cubic_profile(r, param, sharpness);
|
|
}
|
|
else {
|
|
return gaussian_profile(r, param);
|
|
}
|
|
}
|
|
|
|
/* Resolution for each sample of the precomputed kernel profile */
|
|
#define INTEGRAL_RESOLUTION 32
|
|
static float eval_integral(float x0, float x1, short falloff_type, float sharpness, float param)
|
|
{
|
|
const float range = x1 - x0;
|
|
const float step = range / INTEGRAL_RESOLUTION;
|
|
float integral = 0.0f;
|
|
|
|
for (int i = 0; i < INTEGRAL_RESOLUTION; ++i) {
|
|
float x = x0 + range * ((float)i + 0.5f) / (float)INTEGRAL_RESOLUTION;
|
|
float y = eval_profile(x, falloff_type, sharpness, param);
|
|
integral += y * step;
|
|
}
|
|
|
|
return integral;
|
|
}
|
|
#undef INTEGRAL_RESOLUTION
|
|
|
|
static void compute_sss_kernel(
|
|
GPUSssKernelData *kd, float *radii, int sample_ct, int falloff_type, float sharpness)
|
|
{
|
|
float rad[3];
|
|
/* Minimum radius */
|
|
rad[0] = MAX2(radii[0], 1e-15f);
|
|
rad[1] = MAX2(radii[1], 1e-15f);
|
|
rad[2] = MAX2(radii[2], 1e-15f);
|
|
|
|
/* Christensen-Burley fitting */
|
|
float l[3], d[3];
|
|
|
|
if (falloff_type == SHD_SUBSURFACE_BURLEY ||
|
|
falloff_type == SHD_SUBSURFACE_RANDOM_WALK)
|
|
{
|
|
mul_v3_v3fl(l, rad, 0.25f * M_1_PI);
|
|
const float A = 1.0f;
|
|
const float s = 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f);
|
|
/* XXX 0.6f Out of nowhere to match cycles! Empirical! Can be tweak better. */
|
|
mul_v3_v3fl(d, l, 0.6f / s);
|
|
mul_v3_v3fl(rad, d, BURLEY_TRUNCATE);
|
|
kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
|
|
|
|
copy_v3_v3(kd->param, d);
|
|
}
|
|
else if (falloff_type == SHD_SUBSURFACE_CUBIC) {
|
|
copy_v3_v3(kd->param, rad);
|
|
mul_v3_fl(rad, 1.0f + sharpness);
|
|
kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
|
|
}
|
|
else {
|
|
kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
|
|
|
|
copy_v3_v3(kd->param, rad);
|
|
}
|
|
|
|
/* Compute samples locations on the 1d kernel [-1..1] */
|
|
sss_calculate_offsets(kd, sample_ct, SSS_EXPONENT);
|
|
|
|
/* Weights sum for normalization */
|
|
float sum[3] = {0.0f, 0.0f, 0.0f};
|
|
|
|
/* Compute integral of each sample footprint */
|
|
for (int i = 0; i < sample_ct; i++) {
|
|
float x0, x1;
|
|
|
|
if (i == 0) {
|
|
x0 = kd->kernel[0][3] - fabsf(kd->kernel[0][3] - kd->kernel[1][3]) / 2.0f;
|
|
}
|
|
else {
|
|
x0 = (kd->kernel[i - 1][3] + kd->kernel[i][3]) / 2.0f;
|
|
}
|
|
|
|
if (i == sample_ct - 1) {
|
|
x1 = kd->kernel[sample_ct - 1][3] + fabsf(kd->kernel[sample_ct - 2][3] - kd->kernel[sample_ct - 1][3]) / 2.0f;
|
|
}
|
|
else {
|
|
x1 = (kd->kernel[i][3] + kd->kernel[i + 1][3]) / 2.0f;
|
|
}
|
|
|
|
x0 *= kd->max_radius;
|
|
x1 *= kd->max_radius;
|
|
|
|
kd->kernel[i][0] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[0]);
|
|
kd->kernel[i][1] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[1]);
|
|
kd->kernel[i][2] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[2]);
|
|
|
|
sum[0] += kd->kernel[i][0];
|
|
sum[1] += kd->kernel[i][1];
|
|
sum[2] += kd->kernel[i][2];
|
|
}
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
if (sum[i] > 0.0f) {
|
|
/* Normalize */
|
|
for (int j = 0; j < sample_ct; j++) {
|
|
kd->kernel[j][i] /= sum[i];
|
|
}
|
|
}
|
|
else {
|
|
/* Avoid 0 kernel sum. */
|
|
kd->kernel[sample_ct / 2][i] = 1.0f;
|
|
}
|
|
}
|
|
|
|
/* Put center sample at the start of the array (to sample first) */
|
|
float tmpv[4];
|
|
copy_v4_v4(tmpv, kd->kernel[sample_ct / 2]);
|
|
for (int i = sample_ct / 2; i > 0; i--) {
|
|
copy_v4_v4(kd->kernel[i], kd->kernel[i - 1]);
|
|
}
|
|
copy_v4_v4(kd->kernel[0], tmpv);
|
|
|
|
kd->samples = sample_ct;
|
|
}
|
|
|
|
#define INTEGRAL_RESOLUTION 512
|
|
static void compute_sss_translucence_kernel(
|
|
const GPUSssKernelData *kd, int resolution, short falloff_type, float sharpness, float **output)
|
|
{
|
|
float (*texels)[4];
|
|
texels = MEM_callocN(sizeof(float) * 4 * resolution, "compute_sss_translucence_kernel");
|
|
*output = (float *)texels;
|
|
|
|
/* Last texel should be black, hence the - 1. */
|
|
for (int i = 0; i < resolution - 1; ++i) {
|
|
/* Distance from surface. */
|
|
float d = kd->max_radius * ((float)i + 0.00001f) / ((float)resolution);
|
|
|
|
/* For each distance d we compute the radiance incomming from an hypothetic parallel plane. */
|
|
/* Compute radius of the footprint on the hypothetic plane */
|
|
float r_fp = sqrtf(kd->max_radius * kd->max_radius - d * d);
|
|
float r_step = r_fp / INTEGRAL_RESOLUTION;
|
|
float area_accum = 0.0f;
|
|
for (float r = 0.0f; r < r_fp; r += r_step) {
|
|
/* Compute distance to the "shading" point through the medium. */
|
|
/* r_step * 0.5f to put sample between the area borders */
|
|
float dist = hypotf(r + r_step * 0.5f, d);
|
|
|
|
float profile[3];
|
|
profile[0] = eval_profile(dist, falloff_type, sharpness, kd->param[0]);
|
|
profile[1] = eval_profile(dist, falloff_type, sharpness, kd->param[1]);
|
|
profile[2] = eval_profile(dist, falloff_type, sharpness, kd->param[2]);
|
|
|
|
/* Since the profile and configuration are radially symetrical we
|
|
* can just evaluate it once and weight it accordingly */
|
|
float r_next = r + r_step;
|
|
float disk_area = (M_PI * r_next * r_next) - (M_PI * r * r);
|
|
|
|
mul_v3_fl(profile, disk_area);
|
|
add_v3_v3(texels[i], profile);
|
|
area_accum += disk_area;
|
|
}
|
|
/* Normalize over the disk. */
|
|
mul_v3_fl(texels[i], 1.0f / (area_accum));
|
|
}
|
|
|
|
/* Normalize */
|
|
for (int j = resolution - 2; j > 0; j--) {
|
|
texels[j][0] /= (texels[0][0] > 0.0f) ? texels[0][0] : 1.0f;
|
|
texels[j][1] /= (texels[0][1] > 0.0f) ? texels[0][1] : 1.0f;
|
|
texels[j][2] /= (texels[0][2] > 0.0f) ? texels[0][2] : 1.0f;
|
|
}
|
|
|
|
/* First texel should be white */
|
|
texels[0][0] = (texels[0][0] > 0.0f) ? 1.0f : 0.0f;
|
|
texels[0][1] = (texels[0][1] > 0.0f) ? 1.0f : 0.0f;
|
|
texels[0][2] = (texels[0][2] > 0.0f) ? 1.0f : 0.0f;
|
|
|
|
/* dim the last few texels for smoother transition */
|
|
mul_v3_fl(texels[resolution - 2], 0.25f);
|
|
mul_v3_fl(texels[resolution - 3], 0.5f);
|
|
mul_v3_fl(texels[resolution - 4], 0.75f);
|
|
}
|
|
#undef INTEGRAL_RESOLUTION
|
|
|
|
void GPU_material_sss_profile_create(GPUMaterial *material, float *radii, short *falloff_type, float *sharpness)
|
|
{
|
|
material->sss_radii = radii;
|
|
material->sss_falloff = falloff_type;
|
|
material->sss_sharpness = sharpness;
|
|
material->sss_dirty = true;
|
|
|
|
/* Update / Create UBO */
|
|
if (material->sss_profile == NULL) {
|
|
material->sss_profile = GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL);
|
|
}
|
|
}
|
|
|
|
struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, int sample_ct, GPUTexture **tex_profile)
|
|
{
|
|
if (material->sss_radii == NULL)
|
|
return NULL;
|
|
|
|
if (material->sss_dirty || (material->sss_samples != sample_ct)) {
|
|
GPUSssKernelData kd;
|
|
|
|
float sharpness = (material->sss_sharpness != NULL) ? *material->sss_sharpness : 0.0f;
|
|
|
|
/* XXX Black magic but it seems to fit. Maybe because we integrate -1..1 */
|
|
sharpness *= 0.5f;
|
|
|
|
compute_sss_kernel(&kd, material->sss_radii, sample_ct, *material->sss_falloff, sharpness);
|
|
|
|
/* Update / Create UBO */
|
|
GPU_uniformbuffer_update(material->sss_profile, &kd);
|
|
|
|
/* Update / Create Tex */
|
|
float *translucence_profile;
|
|
compute_sss_translucence_kernel(&kd, 64, *material->sss_falloff, sharpness, &translucence_profile);
|
|
|
|
if (material->sss_tex_profile != NULL) {
|
|
GPU_texture_free(material->sss_tex_profile);
|
|
}
|
|
|
|
material->sss_tex_profile = GPU_texture_create_1D_custom(64, 4, GPU_RGBA16F, translucence_profile, NULL);
|
|
|
|
MEM_freeN(translucence_profile);
|
|
|
|
material->sss_samples = sample_ct;
|
|
material->sss_dirty = false;
|
|
}
|
|
|
|
if (tex_profile != NULL) {
|
|
*tex_profile = material->sss_tex_profile;
|
|
}
|
|
return material->sss_profile;
|
|
}
|
|
|
|
#undef SSS_EXPONENT
|
|
#undef SSS_SAMPLES
|
|
|
|
void GPU_material_vertex_attributes(GPUMaterial *material, GPUVertexAttribs *attribs)
|
|
{
|
|
*attribs = material->attribs;
|
|
}
|
|
|
|
void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link)
|
|
{
|
|
if (!material->outlink)
|
|
material->outlink = link;
|
|
}
|
|
|
|
void GPU_material_enable_alpha(GPUMaterial *material)
|
|
{
|
|
material->alpha = 1;
|
|
}
|
|
|
|
GPUBlendMode GPU_material_alpha_blend(GPUMaterial *material, float obcol[4])
|
|
{
|
|
if (material->alpha || (material->obcolalpha && obcol[3] < 1.0f))
|
|
return GPU_BLEND_ALPHA;
|
|
else
|
|
return GPU_BLEND_SOLID;
|
|
}
|
|
|
|
void gpu_material_add_node(GPUMaterial *material, GPUNode *node)
|
|
{
|
|
BLI_addtail(&material->nodes, node);
|
|
}
|
|
|
|
/* Return true if the material compilation has not yet begin or begin. */
|
|
GPUMaterialStatus GPU_material_status(GPUMaterial *mat)
|
|
{
|
|
return mat->status;
|
|
}
|
|
|
|
/* Code generation */
|
|
|
|
bool GPU_material_do_color_management(GPUMaterial *mat)
|
|
{
|
|
if (!BKE_scene_check_color_management_enabled(mat->scene))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GPU_material_use_new_shading_nodes(GPUMaterial *mat)
|
|
{
|
|
return BKE_scene_use_new_shading_nodes(mat->scene);
|
|
}
|
|
|
|
bool GPU_material_use_world_space_shading(GPUMaterial *mat)
|
|
{
|
|
return BKE_scene_use_world_space_shading(mat->scene);
|
|
}
|
|
|
|
bool GPU_material_use_domain_surface(GPUMaterial *mat)
|
|
{
|
|
return (mat->domain & GPU_DOMAIN_SURFACE);
|
|
}
|
|
|
|
bool GPU_material_use_domain_volume(GPUMaterial *mat)
|
|
{
|
|
return (mat->domain & GPU_DOMAIN_VOLUME);
|
|
}
|
|
|
|
static GPUNodeLink *lamp_get_visibility(GPUMaterial *mat, GPULamp *lamp, GPUNodeLink **lv, GPUNodeLink **dist)
|
|
{
|
|
GPUNodeLink *visifac;
|
|
|
|
/* from get_lamp_visibility */
|
|
if (lamp->type == LA_SUN || lamp->type == LA_HEMI) {
|
|
mat->dynproperty |= DYN_LAMP_VEC;
|
|
GPU_link(mat, "lamp_visibility_sun_hemi",
|
|
GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob), lv, dist, &visifac);
|
|
return visifac;
|
|
}
|
|
else {
|
|
mat->dynproperty |= DYN_LAMP_CO;
|
|
GPU_link(mat, "lamp_visibility_other",
|
|
GPU_builtin(GPU_VIEW_POSITION),
|
|
GPU_dynamic_uniform(lamp->dynco, GPU_DYNAMIC_LAMP_DYNCO, lamp->ob), lv, dist, &visifac);
|
|
|
|
if (lamp->type == LA_AREA)
|
|
return visifac;
|
|
|
|
switch (lamp->falloff_type) {
|
|
case LA_FALLOFF_CONSTANT:
|
|
break;
|
|
case LA_FALLOFF_INVLINEAR:
|
|
GPU_link(mat, "lamp_falloff_invlinear",
|
|
GPU_dynamic_uniform(&lamp->dist, GPU_DYNAMIC_LAMP_DISTANCE, lamp->ob), *dist, &visifac);
|
|
break;
|
|
case LA_FALLOFF_INVSQUARE:
|
|
GPU_link(mat, "lamp_falloff_invsquare",
|
|
GPU_dynamic_uniform(&lamp->dist, GPU_DYNAMIC_LAMP_DISTANCE, lamp->ob), *dist, &visifac);
|
|
break;
|
|
case LA_FALLOFF_SLIDERS:
|
|
GPU_link(mat, "lamp_falloff_sliders",
|
|
GPU_dynamic_uniform(&lamp->dist, GPU_DYNAMIC_LAMP_DISTANCE, lamp->ob),
|
|
GPU_dynamic_uniform(&lamp->att1, GPU_DYNAMIC_LAMP_ATT1, lamp->ob),
|
|
GPU_dynamic_uniform(&lamp->att2, GPU_DYNAMIC_LAMP_ATT2, lamp->ob), *dist, &visifac);
|
|
break;
|
|
case LA_FALLOFF_INVCOEFFICIENTS:
|
|
GPU_link(mat, "lamp_falloff_invcoefficients",
|
|
GPU_dynamic_uniform(&lamp->coeff_const, GPU_DYNAMIC_LAMP_COEFFCONST, lamp->ob),
|
|
GPU_dynamic_uniform(&lamp->coeff_lin, GPU_DYNAMIC_LAMP_COEFFLIN, lamp->ob),
|
|
GPU_dynamic_uniform(&lamp->coeff_quad, GPU_DYNAMIC_LAMP_COEFFQUAD, lamp->ob), *dist, &visifac);
|
|
break;
|
|
case LA_FALLOFF_CURVE:
|
|
{
|
|
float *array;
|
|
int size;
|
|
|
|
curvemapping_initialize(lamp->curfalloff);
|
|
curvemapping_table_RGBA(lamp->curfalloff, &array, &size);
|
|
GPU_link(mat, "lamp_falloff_curve",
|
|
GPU_dynamic_uniform(&lamp->dist, GPU_DYNAMIC_LAMP_DISTANCE, lamp->ob),
|
|
GPU_texture(size, array), *dist, &visifac);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (lamp->mode & LA_SPHERE)
|
|
GPU_link(mat, "lamp_visibility_sphere",
|
|
GPU_dynamic_uniform(&lamp->dist, GPU_DYNAMIC_LAMP_DISTANCE, lamp->ob),
|
|
*dist, visifac, &visifac);
|
|
|
|
if (lamp->type == LA_SPOT) {
|
|
GPUNodeLink *inpr;
|
|
|
|
if (lamp->mode & LA_SQUARE) {
|
|
mat->dynproperty |= DYN_LAMP_VEC | DYN_LAMP_IMAT;
|
|
GPU_link(mat, "lamp_visibility_spot_square",
|
|
GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob),
|
|
GPU_dynamic_uniform((float *)lamp->dynimat, GPU_DYNAMIC_LAMP_DYNIMAT, lamp->ob),
|
|
GPU_dynamic_uniform((float *)lamp->spotvec, GPU_DYNAMIC_LAMP_SPOTSCALE, lamp->ob), *lv, &inpr);
|
|
}
|
|
else {
|
|
mat->dynproperty |= DYN_LAMP_VEC | DYN_LAMP_IMAT;
|
|
GPU_link(mat, "lamp_visibility_spot_circle",
|
|
GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob),
|
|
GPU_dynamic_uniform((float *)lamp->dynimat, GPU_DYNAMIC_LAMP_DYNIMAT, lamp->ob),
|
|
GPU_dynamic_uniform((float *)lamp->spotvec, GPU_DYNAMIC_LAMP_SPOTSCALE, lamp->ob), *lv, &inpr);
|
|
}
|
|
|
|
GPU_link(mat, "lamp_visibility_spot",
|
|
GPU_dynamic_uniform(&lamp->spotsi, GPU_DYNAMIC_LAMP_SPOTSIZE, lamp->ob),
|
|
GPU_dynamic_uniform(&lamp->spotbl, GPU_DYNAMIC_LAMP_SPOTBLEND, lamp->ob),
|
|
inpr, visifac, &visifac);
|
|
}
|
|
|
|
GPU_link(mat, "lamp_visibility_clamp", visifac, &visifac);
|
|
|
|
return visifac;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static void area_lamp_vectors(LampRen *lar)
|
|
{
|
|
float xsize = 0.5f * lar->area_size, ysize = 0.5f * lar->area_sizey;
|
|
|
|
/* make it smaller, so area light can be multisampled */
|
|
float multifac = 1.0f / sqrtf((float)lar->ray_totsamp);
|
|
xsize *= multifac;
|
|
ysize *= multifac;
|
|
|
|
/* corner vectors */
|
|
lar->area[0][0] = lar->co[0] - xsize * lar->mat[0][0] - ysize * lar->mat[1][0];
|
|
lar->area[0][1] = lar->co[1] - xsize * lar->mat[0][1] - ysize * lar->mat[1][1];
|
|
lar->area[0][2] = lar->co[2] - xsize * lar->mat[0][2] - ysize * lar->mat[1][2];
|
|
|
|
/* corner vectors */
|
|
lar->area[1][0] = lar->co[0] - xsize * lar->mat[0][0] + ysize * lar->mat[1][0];
|
|
lar->area[1][1] = lar->co[1] - xsize * lar->mat[0][1] + ysize * lar->mat[1][1];
|
|
lar->area[1][2] = lar->co[2] - xsize * lar->mat[0][2] + ysize * lar->mat[1][2];
|
|
|
|
/* corner vectors */
|
|
lar->area[2][0] = lar->co[0] + xsize * lar->mat[0][0] + ysize * lar->mat[1][0];
|
|
lar->area[2][1] = lar->co[1] + xsize * lar->mat[0][1] + ysize * lar->mat[1][1];
|
|
lar->area[2][2] = lar->co[2] + xsize * lar->mat[0][2] + ysize * lar->mat[1][2];
|
|
|
|
/* corner vectors */
|
|
lar->area[3][0] = lar->co[0] + xsize * lar->mat[0][0] - ysize * lar->mat[1][0];
|
|
lar->area[3][1] = lar->co[1] + xsize * lar->mat[0][1] - ysize * lar->mat[1][1];
|
|
lar->area[3][2] = lar->co[2] + xsize * lar->mat[0][2] - ysize * lar->mat[1][2];
|
|
/* only for correction button size, matrix size works on energy */
|
|
lar->areasize = lar->dist * lar->dist / (4.0f * xsize * ysize);
|
|
}
|
|
#endif
|
|
|
|
static void ramp_blend(
|
|
GPUMaterial *mat, GPUNodeLink *fac, GPUNodeLink *col1, GPUNodeLink *col2, int type,
|
|
GPUNodeLink **r_col)
|
|
{
|
|
static const char *names[] = {"mix_blend", "mix_add", "mix_mult", "mix_sub",
|
|
"mix_screen", "mix_div", "mix_diff", "mix_dark", "mix_light",
|
|
"mix_overlay", "mix_dodge", "mix_burn", "mix_hue", "mix_sat",
|
|
"mix_val", "mix_color", "mix_soft", "mix_linear"};
|
|
|
|
GPU_link(mat, names[type], fac, col1, col2, r_col);
|
|
}
|
|
|
|
static void BKE_colorband_eval_blend(
|
|
GPUMaterial *mat, ColorBand *coba, GPUNodeLink *fac, float rampfac, int type,
|
|
GPUNodeLink *incol, GPUNodeLink **r_col)
|
|
{
|
|
GPUNodeLink *tmp, *alpha, *col;
|
|
float *array;
|
|
int size;
|
|
|
|
/* do colorband */
|
|
BKE_colorband_evaluate_table_rgba(coba, &array, &size);
|
|
GPU_link(mat, "valtorgb", fac, GPU_texture(size, array), &col, &tmp);
|
|
|
|
/* use alpha in fac */
|
|
GPU_link(mat, "mtex_alpha_from_col", col, &alpha);
|
|
GPU_link(mat, "math_multiply", alpha, GPU_uniform(&rampfac), &fac);
|
|
|
|
/* blending method */
|
|
ramp_blend(mat, fac, incol, col, type, r_col);
|
|
}
|
|
|
|
static void ramp_diffuse_result(GPUShadeInput *shi, GPUNodeLink **diff)
|
|
{
|
|
Material *ma = shi->mat;
|
|
GPUMaterial *mat = shi->gpumat;
|
|
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_RAMPS)) {
|
|
if (ma->ramp_col) {
|
|
if (ma->rampin_col == MA_RAMP_IN_RESULT) {
|
|
GPUNodeLink *fac;
|
|
GPU_link(mat, "ramp_rgbtobw", *diff, &fac);
|
|
|
|
/* colorband + blend */
|
|
BKE_colorband_eval_blend(mat, ma->ramp_col, fac, ma->rampfac_col, ma->rampblend_col, *diff, diff);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void add_to_diffuse(
|
|
GPUMaterial *mat, Material *ma, GPUShadeInput *shi, GPUNodeLink *is, GPUNodeLink *rgb,
|
|
GPUNodeLink **r_diff)
|
|
{
|
|
GPUNodeLink *fac, *tmp, *addcol;
|
|
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_RAMPS) &&
|
|
ma->ramp_col && (ma->mode & MA_RAMP_COL))
|
|
{
|
|
/* MA_RAMP_IN_RESULT is exceptional */
|
|
if (ma->rampin_col == MA_RAMP_IN_RESULT) {
|
|
addcol = shi->rgb;
|
|
}
|
|
else {
|
|
/* input */
|
|
switch (ma->rampin_col) {
|
|
case MA_RAMP_IN_ENERGY:
|
|
GPU_link(mat, "ramp_rgbtobw", rgb, &fac);
|
|
break;
|
|
case MA_RAMP_IN_SHADER:
|
|
fac = is;
|
|
break;
|
|
case MA_RAMP_IN_NOR:
|
|
GPU_link(mat, "vec_math_dot", shi->view, shi->vn, &tmp, &fac);
|
|
break;
|
|
default:
|
|
GPU_link(mat, "set_value_zero", &fac);
|
|
break;
|
|
}
|
|
|
|
/* colorband + blend */
|
|
BKE_colorband_eval_blend(mat, ma->ramp_col, fac, ma->rampfac_col, ma->rampblend_col, shi->rgb, &addcol);
|
|
}
|
|
}
|
|
else
|
|
addcol = shi->rgb;
|
|
|
|
/* output to */
|
|
GPU_link(mat, "shade_madd", *r_diff, rgb, addcol, r_diff);
|
|
}
|
|
|
|
static void ramp_spec_result(GPUShadeInput *shi, GPUNodeLink **spec)
|
|
{
|
|
Material *ma = shi->mat;
|
|
GPUMaterial *mat = shi->gpumat;
|
|
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_RAMPS) &&
|
|
ma->ramp_spec && ma->rampin_spec == MA_RAMP_IN_RESULT)
|
|
{
|
|
GPUNodeLink *fac;
|
|
GPU_link(mat, "ramp_rgbtobw", *spec, &fac);
|
|
|
|
/* colorband + blend */
|
|
BKE_colorband_eval_blend(mat, ma->ramp_spec, fac, ma->rampfac_spec, ma->rampblend_spec, *spec, spec);
|
|
}
|
|
}
|
|
|
|
static void do_specular_ramp(GPUShadeInput *shi, GPUNodeLink *is, GPUNodeLink *t, GPUNodeLink **spec)
|
|
{
|
|
Material *ma = shi->mat;
|
|
GPUMaterial *mat = shi->gpumat;
|
|
GPUNodeLink *fac, *tmp;
|
|
|
|
*spec = shi->specrgb;
|
|
|
|
/* MA_RAMP_IN_RESULT is exception */
|
|
if (ma->ramp_spec && (ma->rampin_spec != MA_RAMP_IN_RESULT)) {
|
|
|
|
/* input */
|
|
switch (ma->rampin_spec) {
|
|
case MA_RAMP_IN_ENERGY:
|
|
fac = t;
|
|
break;
|
|
case MA_RAMP_IN_SHADER:
|
|
fac = is;
|
|
break;
|
|
case MA_RAMP_IN_NOR:
|
|
GPU_link(mat, "vec_math_dot", shi->view, shi->vn, &tmp, &fac);
|
|
break;
|
|
default:
|
|
GPU_link(mat, "set_value_zero", &fac);
|
|
break;
|
|
}
|
|
|
|
/* colorband + blend */
|
|
BKE_colorband_eval_blend(mat, ma->ramp_spec, fac, ma->rampfac_spec, ma->rampblend_spec, *spec, spec);
|
|
}
|
|
}
|
|
|
|
static void add_user_list(ListBase *list, void *data)
|
|
{
|
|
LinkData *link = MEM_callocN(sizeof(LinkData), "GPULinkData");
|
|
link->data = data;
|
|
BLI_addtail(list, link);
|
|
}
|
|
|
|
static void shade_light_textures(GPUMaterial *mat, GPULamp *lamp, GPUNodeLink **rgb)
|
|
{
|
|
for (int i = 0; i < MAX_MTEX; ++i) {
|
|
MTex *mtex = lamp->la->mtex[i];
|
|
|
|
if (mtex && mtex->tex && (mtex->tex->type & TEX_IMAGE) && mtex->tex->ima) {
|
|
mat->dynproperty |= DYN_LAMP_PERSMAT;
|
|
|
|
float one = 1.0f;
|
|
GPUNodeLink *tex_rgb;
|
|
|
|
GPU_link(mat, "shade_light_texture",
|
|
GPU_builtin(GPU_VIEW_POSITION),
|
|
GPU_image(mtex->tex->ima, &mtex->tex->iuser, false),
|
|
GPU_dynamic_uniform((float *)lamp->dynpersmat, GPU_DYNAMIC_LAMP_DYNPERSMAT, lamp->ob),
|
|
&tex_rgb);
|
|
texture_rgb_blend(mat, tex_rgb, *rgb, GPU_uniform(&one), GPU_uniform(&mtex->colfac), mtex->blendtype, rgb);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void shade_one_light(GPUShadeInput *shi, GPUShadeResult *shr, GPULamp *lamp)
|
|
{
|
|
Material *ma = shi->mat;
|
|
GPUMaterial *mat = shi->gpumat;
|
|
GPUNodeLink *lv, *dist, *is, *inp, *i;
|
|
GPUNodeLink *outcol, *specfac, *t, *shadfac = NULL, *lcol;
|
|
float one = 1.0f;
|
|
|
|
if ((lamp->mode & LA_ONLYSHADOW) && !(ma->mode & MA_SHADOW))
|
|
return;
|
|
|
|
GPUNodeLink *vn = shi->vn;
|
|
GPUNodeLink *view = shi->view;
|
|
|
|
GPUNodeLink *visifac = lamp_get_visibility(mat, lamp, &lv, &dist);
|
|
|
|
#if 0
|
|
if (ma->mode & MA_TANGENT_V)
|
|
GPU_link(mat, "shade_tangent_v", lv, GPU_attribute(CD_TANGENT, ""), &vn);
|
|
#endif
|
|
|
|
GPU_link(mat, "shade_inp", vn, lv, &inp);
|
|
|
|
if (lamp->mode & LA_NO_DIFF) {
|
|
GPU_link(mat, "shade_is_no_diffuse", &is);
|
|
}
|
|
else if (lamp->type == LA_HEMI) {
|
|
GPU_link(mat, "shade_is_hemi", inp, &is);
|
|
}
|
|
else {
|
|
if (lamp->type == LA_AREA) {
|
|
float area[4][4] = {{0.0f}}, areasize = 0.0f;
|
|
|
|
mat->dynproperty |= DYN_LAMP_VEC | DYN_LAMP_CO;
|
|
GPU_link(mat, "shade_inp_area",
|
|
GPU_builtin(GPU_VIEW_POSITION),
|
|
GPU_dynamic_uniform(lamp->dynco, GPU_DYNAMIC_LAMP_DYNCO, lamp->ob),
|
|
GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob), vn,
|
|
GPU_uniform((float *)area),
|
|
GPU_uniform(&areasize),
|
|
GPU_uniform(&lamp->k), &inp);
|
|
}
|
|
|
|
is = inp; /* Lambert */
|
|
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_SHADERS)) {
|
|
if (ma->diff_shader == MA_DIFF_ORENNAYAR)
|
|
GPU_link(mat, "shade_diffuse_oren_nayer", inp, vn, lv, view,
|
|
GPU_uniform(&ma->roughness), &is);
|
|
else if (ma->diff_shader == MA_DIFF_TOON)
|
|
GPU_link(mat, "shade_diffuse_toon", vn, lv, view,
|
|
GPU_uniform(&ma->param[0]), GPU_uniform(&ma->param[1]), &is);
|
|
else if (ma->diff_shader == MA_DIFF_MINNAERT)
|
|
GPU_link(mat, "shade_diffuse_minnaert", inp, vn, view,
|
|
GPU_uniform(&ma->darkness), &is);
|
|
else if (ma->diff_shader == MA_DIFF_FRESNEL)
|
|
GPU_link(mat, "shade_diffuse_fresnel", vn, lv, view,
|
|
GPU_uniform(&ma->param[0]), GPU_uniform(&ma->param[1]), &is);
|
|
}
|
|
}
|
|
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_SHADERS))
|
|
if (ma->shade_flag & MA_CUBIC)
|
|
GPU_link(mat, "shade_cubic", is, &is);
|
|
|
|
i = is;
|
|
GPU_link(mat, "shade_visifac", i, visifac, shi->refl, &i);
|
|
|
|
GPU_link(mat, "set_rgb", GPU_dynamic_uniform(lamp->dyncol, GPU_DYNAMIC_LAMP_DYNCOL, lamp->ob), &lcol);
|
|
shade_light_textures(mat, lamp, &lcol);
|
|
GPU_link(mat, "shade_mul_value_v3",
|
|
GPU_dynamic_uniform(&lamp->dynenergy, GPU_DYNAMIC_LAMP_DYNENERGY, lamp->ob), lcol, &lcol);
|
|
|
|
#if 0
|
|
if (ma->mode & MA_TANGENT_VN)
|
|
GPU_link(mat, "shade_tangent_v_spec", GPU_attribute(CD_TANGENT, ""), &vn);
|
|
#endif
|
|
|
|
/* this replaces if (i > 0.0) conditional until that is supported */
|
|
/* done in shade_visifac now, GPU_link(mat, "mtex_value_clamp_positive", i, &i); */
|
|
|
|
if ((ma->mode & MA_SHADOW) && GPU_lamp_has_shadow_buffer(lamp)) {
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_SHADOWS)) {
|
|
mat->dynproperty |= DYN_LAMP_PERSMAT;
|
|
|
|
if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) {
|
|
GPU_link(mat, "test_shadowbuf_vsm",
|
|
GPU_builtin(GPU_VIEW_POSITION),
|
|
GPU_dynamic_texture(lamp->tex, GPU_DYNAMIC_SAMPLER_2DSHADOW, lamp->ob),
|
|
GPU_dynamic_uniform((float *)lamp->dynpersmat, GPU_DYNAMIC_LAMP_DYNPERSMAT, lamp->ob),
|
|
GPU_uniform(&lamp->bias), GPU_uniform(&lamp->la->bleedbias), inp, &shadfac);
|
|
}
|
|
else {
|
|
GPU_link(mat, "test_shadowbuf",
|
|
GPU_builtin(GPU_VIEW_POSITION),
|
|
GPU_dynamic_texture(lamp->tex, GPU_DYNAMIC_SAMPLER_2DSHADOW, lamp->ob),
|
|
GPU_dynamic_uniform((float *)lamp->dynpersmat, GPU_DYNAMIC_LAMP_DYNPERSMAT, lamp->ob),
|
|
GPU_uniform(&lamp->bias), inp, &shadfac);
|
|
}
|
|
|
|
if (lamp->mode & LA_ONLYSHADOW) {
|
|
GPUNodeLink *shadrgb;
|
|
GPU_link(mat, "shade_only_shadow", i, shadfac,
|
|
GPU_dynamic_uniform(&lamp->dynenergy, GPU_DYNAMIC_LAMP_DYNENERGY, lamp->ob),
|
|
GPU_uniform(lamp->shadow_color), &shadrgb);
|
|
|
|
if (!(lamp->mode & LA_NO_DIFF)) {
|
|
GPU_link(mat, "shade_only_shadow_diffuse", shadrgb, shi->rgb,
|
|
shr->diff, &shr->diff);
|
|
}
|
|
|
|
if (!(lamp->mode & LA_NO_SPEC)) {
|
|
GPU_link(mat, "shade_only_shadow_specular", shadrgb, shi->specrgb,
|
|
shr->spec, &shr->spec);
|
|
}
|
|
|
|
add_user_list(&mat->lamps, lamp);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if ((mat->scene->gm.flag & GAME_GLSL_NO_SHADOWS) && (lamp->mode & LA_ONLYSHADOW)) {
|
|
add_user_list(&mat->lamps, lamp);
|
|
return;
|
|
}
|
|
else
|
|
GPU_link(mat, "set_value", GPU_uniform(&one), &shadfac);
|
|
|
|
if (GPU_link_changed(shi->refl) || ma->ref != 0.0f) {
|
|
if (!(lamp->mode & LA_NO_DIFF)) {
|
|
GPUNodeLink *rgb;
|
|
GPU_link(mat, "shade_mul_value", i, lcol, &rgb);
|
|
GPU_link(mat, "mtex_value_invert", shadfac, &shadfac);
|
|
GPU_link(mat, "mix_mult", shadfac, rgb, GPU_uniform(lamp->shadow_color), &rgb);
|
|
GPU_link(mat, "mtex_value_invert", shadfac, &shadfac);
|
|
add_to_diffuse(mat, ma, shi, is, rgb, &shr->diff);
|
|
}
|
|
}
|
|
|
|
if (mat->scene->gm.flag & GAME_GLSL_NO_SHADERS) {
|
|
/* pass */
|
|
}
|
|
else if (!(lamp->mode & LA_NO_SPEC) && !(lamp->mode & LA_ONLYSHADOW) &&
|
|
(GPU_link_changed(shi->spec) || ma->spec != 0.0f))
|
|
{
|
|
if (lamp->type == LA_HEMI) {
|
|
GPU_link(mat, "shade_hemi_spec", vn, lv, view, GPU_uniform(&ma->spec), shi->har, visifac, &t);
|
|
GPU_link(mat, "shade_add_spec", t, lcol, shi->specrgb, &outcol);
|
|
GPU_link(mat, "shade_add_clamped", shr->spec, outcol, &shr->spec);
|
|
}
|
|
else {
|
|
if (ma->spec_shader == MA_SPEC_PHONG) {
|
|
GPU_link(mat, "shade_phong_spec", vn, lv, view, shi->har, &specfac);
|
|
}
|
|
else if (ma->spec_shader == MA_SPEC_COOKTORR) {
|
|
GPU_link(mat, "shade_cooktorr_spec", vn, lv, view, shi->har, &specfac);
|
|
}
|
|
else if (ma->spec_shader == MA_SPEC_BLINN) {
|
|
GPU_link(mat, "shade_blinn_spec", vn, lv, view,
|
|
GPU_uniform(&ma->refrac), shi->har, &specfac);
|
|
}
|
|
else if (ma->spec_shader == MA_SPEC_WARDISO) {
|
|
GPU_link(mat, "shade_wardiso_spec", vn, lv, view,
|
|
GPU_uniform(&ma->rms), &specfac);
|
|
}
|
|
else {
|
|
GPU_link(mat, "shade_toon_spec", vn, lv, view,
|
|
GPU_uniform(&ma->param[2]), GPU_uniform(&ma->param[3]), &specfac);
|
|
}
|
|
|
|
if (lamp->type == LA_AREA)
|
|
GPU_link(mat, "shade_spec_area_inp", specfac, inp, &specfac);
|
|
|
|
GPU_link(mat, "shade_spec_t", shadfac, shi->spec, visifac, specfac, &t);
|
|
|
|
if (ma->mode & MA_RAMP_SPEC) {
|
|
GPUNodeLink *spec;
|
|
do_specular_ramp(shi, specfac, t, &spec);
|
|
GPU_link(mat, "shade_add_spec", t, lcol, spec, &outcol);
|
|
GPU_link(mat, "shade_add_clamped", shr->spec, outcol, &shr->spec);
|
|
}
|
|
else {
|
|
GPU_link(mat, "shade_add_spec", t, lcol, shi->specrgb, &outcol);
|
|
GPU_link(mat, "shade_add_clamped", shr->spec, outcol, &shr->spec);
|
|
}
|
|
}
|
|
}
|
|
|
|
add_user_list(&mat->lamps, lamp);
|
|
}
|
|
|
|
static void material_lights(GPUShadeInput *shi, GPUShadeResult *shr)
|
|
{
|
|
Base *base;
|
|
Scene *sce_iter;
|
|
|
|
for (SETLOOPER(shi->gpumat->scene, sce_iter, base)) {
|
|
Object *ob = base->object;
|
|
|
|
if (ob->type == OB_LAMP) {
|
|
GPULamp *lamp = GPU_lamp_from_blender(shi->gpumat->scene, ob, NULL);
|
|
if (lamp)
|
|
shade_one_light(shi, shr, lamp);
|
|
}
|
|
|
|
if (ob->transflag & OB_DUPLI) {
|
|
ListBase *lb = object_duplilist(G.main->eval_ctx, shi->gpumat->scene, ob);
|
|
|
|
for (DupliObject *dob = lb->first; dob; dob = dob->next) {
|
|
Object *ob_iter = dob->ob;
|
|
|
|
if (ob_iter->type == OB_LAMP) {
|
|
float omat[4][4];
|
|
copy_m4_m4(omat, ob_iter->obmat);
|
|
copy_m4_m4(ob_iter->obmat, dob->mat);
|
|
|
|
GPULamp *lamp = GPU_lamp_from_blender(shi->gpumat->scene, ob_iter, ob);
|
|
if (lamp)
|
|
shade_one_light(shi, shr, lamp);
|
|
|
|
copy_m4_m4(ob_iter->obmat, omat);
|
|
}
|
|
}
|
|
|
|
free_object_duplilist(lb);
|
|
}
|
|
}
|
|
|
|
/* prevent only shadow lamps from producing negative colors.*/
|
|
GPU_link(shi->gpumat, "shade_clamp_positive", shr->spec, &shr->spec);
|
|
GPU_link(shi->gpumat, "shade_clamp_positive", shr->diff, &shr->diff);
|
|
}
|
|
|
|
static void texture_rgb_blend(
|
|
GPUMaterial *mat, GPUNodeLink *tex, GPUNodeLink *out, GPUNodeLink *fact, GPUNodeLink *facg,
|
|
int blendtype, GPUNodeLink **in)
|
|
{
|
|
switch (blendtype) {
|
|
case MTEX_BLEND:
|
|
GPU_link(mat, "mtex_rgb_blend", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_MUL:
|
|
GPU_link(mat, "mtex_rgb_mul", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_SCREEN:
|
|
GPU_link(mat, "mtex_rgb_screen", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_OVERLAY:
|
|
GPU_link(mat, "mtex_rgb_overlay", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_SUB:
|
|
GPU_link(mat, "mtex_rgb_sub", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_ADD:
|
|
GPU_link(mat, "mtex_rgb_add", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_DIV:
|
|
GPU_link(mat, "mtex_rgb_div", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_DIFF:
|
|
GPU_link(mat, "mtex_rgb_diff", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_DARK:
|
|
GPU_link(mat, "mtex_rgb_dark", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_LIGHT:
|
|
GPU_link(mat, "mtex_rgb_light", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_BLEND_HUE:
|
|
GPU_link(mat, "mtex_rgb_hue", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_BLEND_SAT:
|
|
GPU_link(mat, "mtex_rgb_sat", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_BLEND_VAL:
|
|
GPU_link(mat, "mtex_rgb_val", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_BLEND_COLOR:
|
|
GPU_link(mat, "mtex_rgb_color", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_SOFT_LIGHT:
|
|
GPU_link(mat, "mtex_rgb_soft", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_LIN_LIGHT:
|
|
GPU_link(mat, "mtex_rgb_linear", out, tex, fact, facg, in);
|
|
break;
|
|
default:
|
|
GPU_link(mat, "set_rgb_zero", &in);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void texture_value_blend(
|
|
GPUMaterial *mat, GPUNodeLink *tex, GPUNodeLink *out, GPUNodeLink *fact, GPUNodeLink *facg,
|
|
int blendtype, GPUNodeLink **in)
|
|
{
|
|
switch (blendtype) {
|
|
case MTEX_BLEND:
|
|
GPU_link(mat, "mtex_value_blend", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_MUL:
|
|
GPU_link(mat, "mtex_value_mul", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_SCREEN:
|
|
GPU_link(mat, "mtex_value_screen", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_SUB:
|
|
GPU_link(mat, "mtex_value_sub", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_ADD:
|
|
GPU_link(mat, "mtex_value_add", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_DIV:
|
|
GPU_link(mat, "mtex_value_div", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_DIFF:
|
|
GPU_link(mat, "mtex_value_diff", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_DARK:
|
|
GPU_link(mat, "mtex_value_dark", out, tex, fact, facg, in);
|
|
break;
|
|
case MTEX_LIGHT:
|
|
GPU_link(mat, "mtex_value_light", out, tex, fact, facg, in);
|
|
break;
|
|
default:
|
|
GPU_link(mat, "set_value_zero", &in);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void do_material_tex(GPUShadeInput *shi)
|
|
{
|
|
Material *ma = shi->mat;
|
|
GPUMaterial *mat = shi->gpumat;
|
|
MTex *mtex;
|
|
Tex *tex;
|
|
GPUNodeLink *texco, *tin, *trgb, *tnor, *tcol, *stencil, *tnorfac;
|
|
GPUNodeLink *texco_norm, *texco_orco, *texco_object;
|
|
GPUNodeLink *texco_global, *texco_uv = NULL;
|
|
GPUNodeLink *newnor, *orn;
|
|
float one = 1.0f;
|
|
int rgbnor, talpha;
|
|
bool init_done = false;
|
|
int iBumpSpacePrev = 0; /* Not necessary, quieting gcc warning. */
|
|
GPUNodeLink *vNorg, *vNacc, *fPrevMagnitude;
|
|
int iFirstTimeNMap = 1;
|
|
bool found_deriv_map = false;
|
|
|
|
GPU_link(mat, "set_value", GPU_uniform(&one), &stencil);
|
|
|
|
GPU_link(mat, "texco_norm", GPU_builtin(GPU_VIEW_NORMAL), &texco_norm);
|
|
GPU_link(mat, "texco_orco", GPU_attribute(CD_ORCO, ""), &texco_orco);
|
|
GPU_link(mat, "texco_object", GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
|
|
GPU_builtin(GPU_INVERSE_OBJECT_MATRIX),
|
|
GPU_builtin(GPU_VIEW_POSITION), &texco_object);
|
|
#if 0
|
|
GPU_link(mat, "texco_tangent", GPU_attribute(CD_TANGENT, ""), &texco_tangent);
|
|
#endif
|
|
GPU_link(mat, "texco_global", GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
|
|
GPU_builtin(GPU_VIEW_POSITION), &texco_global);
|
|
|
|
orn = texco_norm;
|
|
|
|
/* go over texture slots */
|
|
for (int tex_nr = 0; tex_nr < MAX_MTEX; tex_nr++) {
|
|
/* separate tex switching */
|
|
if (ma->septex & (1 << tex_nr)) continue;
|
|
|
|
if (ma->mtex[tex_nr]) {
|
|
mtex = ma->mtex[tex_nr];
|
|
|
|
tex = mtex->tex;
|
|
if (tex == NULL) continue;
|
|
|
|
/* which coords */
|
|
if (mtex->texco == TEXCO_ORCO)
|
|
texco = texco_orco;
|
|
else if (mtex->texco == TEXCO_OBJECT)
|
|
texco = texco_object;
|
|
else if (mtex->texco == TEXCO_NORM)
|
|
texco = orn;
|
|
else if (mtex->texco == TEXCO_TANGENT)
|
|
texco = texco_object;
|
|
else if (mtex->texco == TEXCO_GLOB)
|
|
texco = texco_global;
|
|
else if (mtex->texco == TEXCO_REFL) {
|
|
GPU_link(mat, "texco_refl", shi->vn, shi->view, &shi->ref);
|
|
texco = shi->ref;
|
|
}
|
|
else if (mtex->texco == TEXCO_UV) {
|
|
if (1) { //!(texco_uv && strcmp(mtex->uvname, lastuvname) == 0)) {
|
|
GPU_link(mat, "texco_uv", GPU_attribute(CD_MTFACE, mtex->uvname), &texco_uv);
|
|
/*lastuvname = mtex->uvname;*/ /*UNUSED*/
|
|
}
|
|
texco = texco_uv;
|
|
}
|
|
else
|
|
continue;
|
|
|
|
/* in case of uv, this would just undo a multiplication in texco_uv */
|
|
if (mtex->texco != TEXCO_UV)
|
|
GPU_link(mat, "mtex_2d_mapping", texco, &texco);
|
|
|
|
if (mtex->size[0] != 1.0f || mtex->size[1] != 1.0f || mtex->size[2] != 1.0f)
|
|
GPU_link(mat, "mtex_mapping_size", texco, GPU_uniform(mtex->size), &texco);
|
|
|
|
float ofs[3] = {
|
|
mtex->ofs[0] + 0.5f - 0.5f * mtex->size[0],
|
|
mtex->ofs[1] + 0.5f - 0.5f * mtex->size[1],
|
|
0.0f
|
|
};
|
|
|
|
if (ofs[0] != 0.0f || ofs[1] != 0.0f || ofs[2] != 0.0f)
|
|
GPU_link(mat, "mtex_mapping_ofs", texco, GPU_uniform(ofs), &texco);
|
|
|
|
talpha = 0;
|
|
|
|
if (tex && tex->ima &&
|
|
((tex->type == TEX_IMAGE) ||
|
|
((tex->type == TEX_ENVMAP) && (mtex->texco == TEXCO_REFL))))
|
|
{
|
|
if (tex->type == TEX_IMAGE) {
|
|
GPU_link(mat, "mtex_image", texco, GPU_image(tex->ima, &tex->iuser, false), &tin, &trgb);
|
|
}
|
|
else {
|
|
GPU_link(mat, "mtex_cube_map_refl",
|
|
GPU_cube_map(tex->ima, &tex->iuser, false), shi->view, shi->vn,
|
|
GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
|
|
GPU_builtin(GPU_VIEW_MATRIX), &tin, &trgb);
|
|
}
|
|
rgbnor = TEX_RGB;
|
|
|
|
talpha = ((tex->imaflag & TEX_USEALPHA) && tex->ima && (tex->ima->flag & IMA_IGNORE_ALPHA) == 0);
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
|
|
/* texture output */
|
|
if ((rgbnor & TEX_RGB) && (mtex->texflag & MTEX_RGBTOINT)) {
|
|
GPU_link(mat, "mtex_rgbtoint", trgb, &tin);
|
|
rgbnor -= TEX_RGB;
|
|
}
|
|
|
|
if (mtex->texflag & MTEX_NEGATIVE) {
|
|
if (rgbnor & TEX_RGB)
|
|
GPU_link(mat, "mtex_rgb_invert", trgb, &trgb);
|
|
else
|
|
GPU_link(mat, "mtex_value_invert", tin, &tin);
|
|
}
|
|
|
|
if (mtex->texflag & MTEX_STENCIL) {
|
|
if (rgbnor & TEX_RGB)
|
|
GPU_link(mat, "mtex_rgb_stencil", stencil, trgb, &stencil, &trgb);
|
|
else
|
|
GPU_link(mat, "mtex_value_stencil", stencil, tin, &stencil, &tin);
|
|
}
|
|
|
|
/* mapping */
|
|
if (mtex->mapto & (MAP_COL | MAP_COLSPEC | MAP_COLMIR)) {
|
|
/* stencil maps on the texture control slider, not texture intensity value */
|
|
if ((rgbnor & TEX_RGB) == 0) {
|
|
GPU_link(mat, "set_rgb", GPU_uniform(&mtex->r), &tcol);
|
|
}
|
|
else {
|
|
GPU_link(mat, "set_rgba", trgb, &tcol);
|
|
|
|
if (mtex->mapto & MAP_ALPHA)
|
|
GPU_link(mat, "set_value", stencil, &tin);
|
|
else if (talpha)
|
|
GPU_link(mat, "mtex_alpha_from_col", trgb, &tin);
|
|
else
|
|
GPU_link(mat, "set_value_one", &tin);
|
|
}
|
|
|
|
if ((tex->type == TEX_IMAGE) ||
|
|
((tex->type == TEX_ENVMAP) && (mtex->texco == TEXCO_REFL)))
|
|
{
|
|
if (GPU_material_do_color_management(mat)) {
|
|
GPU_link(mat, "srgb_to_linearrgb", tcol, &tcol);
|
|
}
|
|
}
|
|
|
|
if (mtex->mapto & MAP_COL) {
|
|
GPUNodeLink *colfac;
|
|
|
|
if (mtex->colfac == 1.0f) colfac = stencil;
|
|
else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->colfac), stencil, &colfac);
|
|
|
|
texture_rgb_blend(mat, tcol, shi->rgb, tin, colfac, mtex->blendtype, &shi->rgb);
|
|
}
|
|
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && (mtex->mapto & MAP_COLSPEC)) {
|
|
GPUNodeLink *colspecfac;
|
|
|
|
if (mtex->colspecfac == 1.0f) colspecfac = stencil;
|
|
else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->colspecfac), stencil, &colspecfac);
|
|
|
|
texture_rgb_blend(mat, tcol, shi->specrgb, tin, colspecfac, mtex->blendtype, &shi->specrgb);
|
|
}
|
|
|
|
if (mtex->mapto & MAP_COLMIR) {
|
|
GPUNodeLink *colmirfac;
|
|
|
|
if (mtex->mirrfac == 1.0f) colmirfac = stencil;
|
|
else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->mirrfac), stencil, &colmirfac);
|
|
|
|
/* exception for envmap only */
|
|
if (tex->type == TEX_ENVMAP && mtex->blendtype == MTEX_BLEND) {
|
|
GPU_link(mat, "mtex_mirror", tcol, shi->refcol, tin, colmirfac, &shi->refcol);
|
|
}
|
|
else
|
|
texture_rgb_blend(mat, tcol, shi->mir, tin, colmirfac, mtex->blendtype, &shi->mir);
|
|
}
|
|
}
|
|
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && (mtex->mapto & MAP_NORM)) {
|
|
if (tex->type == TEX_IMAGE) {
|
|
found_deriv_map = tex->imaflag & TEX_DERIVATIVEMAP;
|
|
|
|
if (tex->imaflag & TEX_NORMALMAP) {
|
|
/* normalmap image */
|
|
GPU_link(mat, "mtex_normal", texco, GPU_image(tex->ima, &tex->iuser, true), &tnor);
|
|
|
|
if (mtex->norfac < 0.0f)
|
|
GPU_link(mat, "mtex_negate_texnormal", tnor, &tnor);
|
|
|
|
if (mtex->normapspace == MTEX_NSPACE_TANGENT) {
|
|
if (iFirstTimeNMap != 0) {
|
|
// use unnormalized normal (this is how we bake it - closer to gamedev)
|
|
GPUNodeLink *vNegNorm;
|
|
GPU_link(mat, "vec_math_negate",
|
|
GPU_builtin(GPU_VIEW_NORMAL), &vNegNorm);
|
|
GPU_link(mat, "mtex_nspace_tangent",
|
|
GPU_attribute(CD_TANGENT, ""), vNegNorm, tnor, &newnor);
|
|
iFirstTimeNMap = 0;
|
|
}
|
|
else { /* otherwise use accumulated perturbations */
|
|
GPU_link(mat, "mtex_nspace_tangent",
|
|
GPU_attribute(CD_TANGENT, ""), shi->vn, tnor, &newnor);
|
|
}
|
|
}
|
|
else if (mtex->normapspace == MTEX_NSPACE_OBJECT) {
|
|
/* transform normal by object then view matrix */
|
|
GPU_link(mat, "mtex_nspace_object", tnor, &newnor);
|
|
}
|
|
else if (mtex->normapspace == MTEX_NSPACE_WORLD) {
|
|
/* transform normal by view matrix */
|
|
GPU_link(mat, "mtex_nspace_world", GPU_builtin(GPU_VIEW_MATRIX), tnor, &newnor);
|
|
}
|
|
else {
|
|
/* no transform, normal in camera space */
|
|
newnor = tnor;
|
|
}
|
|
|
|
float norfac = min_ff(fabsf(mtex->norfac), 1.0f);
|
|
|
|
if (norfac == 1.0f && !GPU_link_changed(stencil)) {
|
|
shi->vn = newnor;
|
|
}
|
|
else {
|
|
tnorfac = GPU_uniform(&norfac);
|
|
|
|
if (GPU_link_changed(stencil))
|
|
GPU_link(mat, "math_multiply", tnorfac, stencil, &tnorfac);
|
|
|
|
GPU_link(mat, "mtex_blend_normal", tnorfac, shi->vn, newnor, &shi->vn);
|
|
}
|
|
|
|
}
|
|
else if (found_deriv_map ||
|
|
(mtex->texflag & (MTEX_3TAP_BUMP | MTEX_5TAP_BUMP | MTEX_BICUBIC_BUMP)))
|
|
{
|
|
/* ntap bumpmap image */
|
|
int iBumpSpace;
|
|
float ima_x, ima_y;
|
|
|
|
float imag_tspace_dimension_x = 1024.0f; /* only used for texture space variant */
|
|
float aspect = 1.0f;
|
|
|
|
GPUNodeLink *vR1, *vR2;
|
|
GPUNodeLink *dBs, *dBt, *fDet;
|
|
|
|
float hScale = 0.1f; /* compatibility adjustment factor for all bumpspace types */
|
|
if (mtex->texflag & MTEX_BUMP_TEXTURESPACE)
|
|
hScale = 13.0f; /* factor for scaling texspace bumps */
|
|
else if (found_deriv_map)
|
|
hScale = 1.0f;
|
|
|
|
/* resolve texture resolution */
|
|
if ((mtex->texflag & MTEX_BUMP_TEXTURESPACE) || found_deriv_map) {
|
|
ImBuf *ibuf = BKE_image_acquire_ibuf(tex->ima, &tex->iuser, NULL);
|
|
ima_x = 512.0f; ima_y = 512.0f; /* prevent calling textureSize, glsl 1.3 only */
|
|
if (ibuf) {
|
|
ima_x = ibuf->x;
|
|
ima_y = ibuf->y;
|
|
aspect = (float)ima_y / ima_x;
|
|
}
|
|
BKE_image_release_ibuf(tex->ima, ibuf, NULL);
|
|
}
|
|
|
|
/* The negate on norfac is done because the
|
|
* normal in the renderer points inward which corresponds
|
|
* to inverting the bump map. Should this ever change
|
|
* this negate must be removed. */
|
|
float norfac = -hScale * mtex->norfac;
|
|
if (found_deriv_map) {
|
|
float fVirtDim = sqrtf(fabsf(ima_x * mtex->size[0] * ima_y * mtex->size[1]));
|
|
norfac /= MAX2(fVirtDim, FLT_EPSILON);
|
|
}
|
|
|
|
tnorfac = GPU_uniform(&norfac);
|
|
|
|
if (found_deriv_map)
|
|
GPU_link(mat, "math_multiply", tnorfac, GPU_builtin(GPU_AUTO_BUMPSCALE), &tnorfac);
|
|
|
|
if (GPU_link_changed(stencil))
|
|
GPU_link(mat, "math_multiply", tnorfac, stencil, &tnorfac);
|
|
|
|
if (!init_done) {
|
|
/* copy shi->vn to vNorg and vNacc, set magnitude to 1 */
|
|
GPU_link(mat, "mtex_bump_normals_init", shi->vn, &vNorg, &vNacc, &fPrevMagnitude);
|
|
iBumpSpacePrev = 0;
|
|
init_done = true;
|
|
}
|
|
|
|
// find current bump space
|
|
if (mtex->texflag & MTEX_BUMP_OBJECTSPACE)
|
|
iBumpSpace = 1;
|
|
else if (mtex->texflag & MTEX_BUMP_TEXTURESPACE)
|
|
iBumpSpace = 2;
|
|
else
|
|
iBumpSpace = 4; /* ViewSpace */
|
|
|
|
/* re-initialize if bump space changed */
|
|
if (iBumpSpacePrev != iBumpSpace) {
|
|
GPUNodeLink *surf_pos = GPU_builtin(GPU_VIEW_POSITION);
|
|
|
|
if (mtex->texflag & MTEX_BUMP_OBJECTSPACE)
|
|
GPU_link(mat, "mtex_bump_init_objspace",
|
|
surf_pos, vNorg,
|
|
GPU_builtin(GPU_VIEW_MATRIX),
|
|
GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
|
|
GPU_builtin(GPU_OBJECT_MATRIX),
|
|
GPU_builtin(GPU_INVERSE_OBJECT_MATRIX),
|
|
fPrevMagnitude, vNacc,
|
|
&fPrevMagnitude, &vNacc,
|
|
&vR1, &vR2, &fDet);
|
|
|
|
else if (mtex->texflag & MTEX_BUMP_TEXTURESPACE)
|
|
GPU_link(mat, "mtex_bump_init_texturespace",
|
|
surf_pos, vNorg,
|
|
fPrevMagnitude, vNacc,
|
|
&fPrevMagnitude, &vNacc,
|
|
&vR1, &vR2, &fDet);
|
|
|
|
else
|
|
GPU_link(mat, "mtex_bump_init_viewspace",
|
|
surf_pos, vNorg,
|
|
fPrevMagnitude, vNacc,
|
|
&fPrevMagnitude, &vNacc,
|
|
&vR1, &vR2, &fDet);
|
|
|
|
iBumpSpacePrev = iBumpSpace;
|
|
}
|
|
|
|
|
|
if (found_deriv_map) {
|
|
GPU_link(mat, "mtex_bump_deriv",
|
|
texco, GPU_image(tex->ima, &tex->iuser, true),
|
|
GPU_uniform(&ima_x), GPU_uniform(&ima_y), tnorfac,
|
|
&dBs, &dBt);
|
|
}
|
|
else if (mtex->texflag & MTEX_3TAP_BUMP)
|
|
GPU_link(mat, "mtex_bump_tap3",
|
|
texco, GPU_image(tex->ima, &tex->iuser, true), tnorfac,
|
|
&dBs, &dBt);
|
|
else if (mtex->texflag & MTEX_5TAP_BUMP)
|
|
GPU_link(mat, "mtex_bump_tap5",
|
|
texco, GPU_image(tex->ima, &tex->iuser, true), tnorfac,
|
|
&dBs, &dBt);
|
|
else if (mtex->texflag & MTEX_BICUBIC_BUMP) {
|
|
if (GPU_bicubic_bump_support()) {
|
|
GPU_link(mat, "mtex_bump_bicubic",
|
|
texco, GPU_image(tex->ima, &tex->iuser, true), tnorfac,
|
|
&dBs, &dBt);
|
|
}
|
|
else {
|
|
GPU_link(mat, "mtex_bump_tap5",
|
|
texco, GPU_image(tex->ima, &tex->iuser, true), tnorfac,
|
|
&dBs, &dBt);
|
|
}
|
|
}
|
|
|
|
|
|
if (mtex->texflag & MTEX_BUMP_TEXTURESPACE) {
|
|
float imag_tspace_dimension_y = aspect * imag_tspace_dimension_x;
|
|
GPU_link(mat, "mtex_bump_apply_texspace",
|
|
fDet, dBs, dBt, vR1, vR2,
|
|
GPU_image(tex->ima, &tex->iuser, true), texco,
|
|
GPU_uniform(&imag_tspace_dimension_x),
|
|
GPU_uniform(&imag_tspace_dimension_y), vNacc,
|
|
&vNacc, &shi->vn);
|
|
}
|
|
else
|
|
GPU_link(mat, "mtex_bump_apply",
|
|
fDet, dBs, dBt, vR1, vR2, vNacc,
|
|
&vNacc, &shi->vn);
|
|
|
|
}
|
|
}
|
|
|
|
GPU_link(mat, "vec_math_negate", shi->vn, &orn);
|
|
}
|
|
|
|
if ((mtex->mapto & MAP_VARS)) {
|
|
if (rgbnor & TEX_RGB) {
|
|
if (talpha)
|
|
GPU_link(mat, "mtex_alpha_from_col", trgb, &tin);
|
|
else
|
|
GPU_link(mat, "mtex_rgbtoint", trgb, &tin);
|
|
}
|
|
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && mtex->mapto & MAP_REF) {
|
|
GPUNodeLink *difffac;
|
|
|
|
if (mtex->difffac == 1.0f) difffac = stencil;
|
|
else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->difffac), stencil, &difffac);
|
|
|
|
texture_value_blend(
|
|
mat, GPU_uniform(&mtex->def_var), shi->refl, tin, difffac,
|
|
mtex->blendtype, &shi->refl);
|
|
GPU_link(mat, "mtex_value_clamp_positive", shi->refl, &shi->refl);
|
|
}
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && mtex->mapto & MAP_SPEC) {
|
|
GPUNodeLink *specfac;
|
|
|
|
if (mtex->specfac == 1.0f) specfac = stencil;
|
|
else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->specfac), stencil, &specfac);
|
|
|
|
texture_value_blend(
|
|
mat, GPU_uniform(&mtex->def_var), shi->spec, tin, specfac,
|
|
mtex->blendtype, &shi->spec);
|
|
GPU_link(mat, "mtex_value_clamp_positive", shi->spec, &shi->spec);
|
|
}
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && mtex->mapto & MAP_EMIT) {
|
|
GPUNodeLink *emitfac;
|
|
|
|
if (mtex->emitfac == 1.0f) emitfac = stencil;
|
|
else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->emitfac), stencil, &emitfac);
|
|
|
|
texture_value_blend(
|
|
mat, GPU_uniform(&mtex->def_var), shi->emit, tin, emitfac,
|
|
mtex->blendtype, &shi->emit);
|
|
GPU_link(mat, "mtex_value_clamp_positive", shi->emit, &shi->emit);
|
|
}
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && mtex->mapto & MAP_HAR) {
|
|
GPUNodeLink *hardfac;
|
|
|
|
if (mtex->hardfac == 1.0f) hardfac = stencil;
|
|
else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->hardfac), stencil, &hardfac);
|
|
|
|
GPU_link(mat, "mtex_har_divide", shi->har, &shi->har);
|
|
texture_value_blend(
|
|
mat, GPU_uniform(&mtex->def_var), shi->har, tin, hardfac,
|
|
mtex->blendtype, &shi->har);
|
|
GPU_link(mat, "mtex_har_multiply_clamp", shi->har, &shi->har);
|
|
}
|
|
if (mtex->mapto & MAP_ALPHA) {
|
|
GPUNodeLink *alphafac;
|
|
|
|
if (mtex->alphafac == 1.0f) alphafac = stencil;
|
|
else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->alphafac), stencil, &alphafac);
|
|
|
|
texture_value_blend(
|
|
mat, GPU_uniform(&mtex->def_var), shi->alpha, tin, alphafac,
|
|
mtex->blendtype, &shi->alpha);
|
|
GPU_link(mat, "mtex_value_clamp", shi->alpha, &shi->alpha);
|
|
}
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && mtex->mapto & MAP_AMB) {
|
|
GPUNodeLink *ambfac;
|
|
|
|
if (mtex->ambfac == 1.0f) ambfac = stencil;
|
|
else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->ambfac), stencil, &ambfac);
|
|
|
|
texture_value_blend(
|
|
mat, GPU_uniform(&mtex->def_var), shi->amb, tin, ambfac,
|
|
mtex->blendtype, &shi->amb);
|
|
GPU_link(mat, "mtex_value_clamp", shi->amb, &shi->amb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GPU_shadeinput_set(GPUMaterial *mat, Material *ma, GPUShadeInput *shi)
|
|
{
|
|
float one = 1.0f;
|
|
|
|
memset(shi, 0, sizeof(*shi));
|
|
|
|
shi->gpumat = mat;
|
|
shi->mat = ma;
|
|
|
|
GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->r, GPU_DYNAMIC_MAT_DIFFRGB, ma), &shi->rgb);
|
|
GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->specr, GPU_DYNAMIC_MAT_SPECRGB, ma), &shi->specrgb);
|
|
GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->mirr, GPU_DYNAMIC_MAT_MIR, ma), &shi->mir);
|
|
GPU_link(mat, "set_rgba_zero", &shi->refcol);
|
|
GPU_link(mat, "shade_norm", GPU_builtin(GPU_VIEW_NORMAL), &shi->vn);
|
|
|
|
if (mat->alpha)
|
|
GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->alpha, GPU_DYNAMIC_MAT_ALPHA, ma), &shi->alpha);
|
|
else
|
|
GPU_link(mat, "set_value", GPU_uniform(&one), &shi->alpha);
|
|
|
|
GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->ref, GPU_DYNAMIC_MAT_REF, ma), &shi->refl);
|
|
GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->spec, GPU_DYNAMIC_MAT_SPEC, ma), &shi->spec);
|
|
GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->emit, GPU_DYNAMIC_MAT_EMIT, ma), &shi->emit);
|
|
GPU_link(mat, "set_value", GPU_dynamic_uniform((float *)&ma->har, GPU_DYNAMIC_MAT_HARD, ma), &shi->har);
|
|
GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->amb, GPU_DYNAMIC_MAT_AMB, ma), &shi->amb);
|
|
GPU_link(mat, "set_value", GPU_uniform(&ma->spectra), &shi->spectra);
|
|
GPU_link(mat, "shade_view", GPU_builtin(GPU_VIEW_POSITION), &shi->view);
|
|
GPU_link(mat, "vcol_attribute", GPU_attribute(CD_MCOL, ""), &shi->vcol);
|
|
if (GPU_material_do_color_management(mat))
|
|
GPU_link(mat, "srgb_to_linearrgb", shi->vcol, &shi->vcol);
|
|
GPU_link(mat, "texco_refl", shi->vn, shi->view, &shi->ref);
|
|
}
|
|
|
|
void GPU_mist_update_enable(short enable)
|
|
{
|
|
GPUWorld.mistenabled = (float)enable;
|
|
}
|
|
|
|
void GPU_mist_update_values(int type, float start, float dist, float inten, float color[3])
|
|
{
|
|
GPUWorld.mistype = (float)type;
|
|
GPUWorld.miststart = start;
|
|
GPUWorld.mistdistance = dist;
|
|
GPUWorld.mistintensity = inten;
|
|
copy_v3_v3(GPUWorld.mistcol, color);
|
|
GPUWorld.mistcol[3] = 1.0f;
|
|
}
|
|
|
|
void GPU_horizon_update_color(float color[3])
|
|
{
|
|
copy_v3_v3(GPUWorld.horicol, color);
|
|
}
|
|
|
|
void GPU_ambient_update_color(float color[3])
|
|
{
|
|
copy_v3_v3(GPUWorld.ambcol, color);
|
|
GPUWorld.ambcol[3] = 1.0f;
|
|
}
|
|
|
|
void GPU_zenith_update_color(float color[3])
|
|
{
|
|
copy_v3_v3(GPUWorld.zencol, color);
|
|
}
|
|
|
|
void GPU_shaderesult_set(GPUShadeInput *shi, GPUShadeResult *shr)
|
|
{
|
|
GPUMaterial *mat = shi->gpumat;
|
|
GPUNodeLink *emit, *ulinfac, *ulogfac, *mistfac;
|
|
Material *ma = shi->mat;
|
|
World *world = mat->scene->world;
|
|
float linfac, logfac;
|
|
|
|
memset(shr, 0, sizeof(*shr));
|
|
|
|
if (ma->mode & MA_VERTEXCOLP)
|
|
shi->rgb = shi->vcol;
|
|
|
|
do_material_tex(shi);
|
|
|
|
if ((mat->scene->gm.flag & GAME_GLSL_NO_LIGHTS) || (ma->mode & MA_SHLESS)) {
|
|
GPU_link(mat, "set_rgb", shi->rgb, &shr->diff);
|
|
GPU_link(mat, "set_rgb_zero", &shr->spec);
|
|
GPU_link(mat, "set_value", shi->alpha, &shr->alpha);
|
|
shr->combined = shr->diff;
|
|
}
|
|
else {
|
|
if (GPU_link_changed(shi->emit) || ma->emit != 0.0f) {
|
|
if ((ma->mode & (MA_VERTEXCOL | MA_VERTEXCOLP)) == MA_VERTEXCOL) {
|
|
GPU_link(mat, "shade_add", shi->emit, shi->vcol, &emit);
|
|
GPU_link(mat, "shade_mul", emit, shi->rgb, &shr->diff);
|
|
}
|
|
else
|
|
GPU_link(mat, "shade_mul_value", shi->emit, shi->rgb, &shr->diff);
|
|
}
|
|
else
|
|
GPU_link(mat, "set_rgb_zero", &shr->diff);
|
|
|
|
GPU_link(mat, "set_rgb_zero", &shr->spec);
|
|
|
|
material_lights(shi, shr);
|
|
|
|
shr->combined = shr->diff;
|
|
|
|
GPU_link(mat, "set_value", shi->alpha, &shr->alpha);
|
|
|
|
if (world) {
|
|
/* exposure correction */
|
|
if (world->exp != 0.0f || world->range != 1.0f) {
|
|
linfac = 1.0f + powf((2.0f * world->exp + 0.5f), -10);
|
|
logfac = logf((linfac - 1.0f) / linfac) / world->range;
|
|
|
|
GPU_link(mat, "set_value", GPU_uniform(&linfac), &ulinfac);
|
|
GPU_link(mat, "set_value", GPU_uniform(&logfac), &ulogfac);
|
|
|
|
GPU_link(mat, "shade_exposure_correct", shr->combined,
|
|
ulinfac, ulogfac, &shr->combined);
|
|
GPU_link(mat, "shade_exposure_correct", shr->spec,
|
|
ulinfac, ulogfac, &shr->spec);
|
|
}
|
|
|
|
/* environment lighting */
|
|
if (!(mat->scene->gm.flag & GAME_GLSL_NO_ENV_LIGHTING) &&
|
|
(world->mode & WO_ENV_LIGHT) &&
|
|
(mat->scene->r.mode & R_SHADOW) &&
|
|
!BKE_scene_use_new_shading_nodes(mat->scene))
|
|
{
|
|
if ((world->ao_env_energy != 0.0f) && (GPU_link_changed(shi->amb) || ma->amb != 0.0f) &&
|
|
(GPU_link_changed(shi->refl) || ma->ref != 0.0f))
|
|
{
|
|
if (world->aocolor != WO_AOPLAIN) {
|
|
if (!(is_zero_v3(&world->horr) & is_zero_v3(&world->zenr))) {
|
|
GPUNodeLink *fcol, *f;
|
|
GPU_link(mat, "math_multiply", shi->amb, shi->refl, &f);
|
|
GPU_link(mat, "math_multiply", f, GPU_uniform(&world->ao_env_energy), &f);
|
|
GPU_link(mat, "shade_mul_value", f, shi->rgb, &fcol);
|
|
GPU_link(mat, "env_apply", shr->combined,
|
|
GPU_dynamic_uniform(GPUWorld.horicol, GPU_DYNAMIC_HORIZON_COLOR, NULL),
|
|
GPU_dynamic_uniform(GPUWorld.zencol, GPU_DYNAMIC_ZENITH_COLOR, NULL), fcol,
|
|
GPU_builtin(GPU_VIEW_MATRIX), shi->vn, &shr->combined);
|
|
}
|
|
}
|
|
else {
|
|
GPUNodeLink *f;
|
|
GPU_link(mat, "math_multiply", shi->amb, shi->refl, &f);
|
|
GPU_link(mat, "math_multiply", f, GPU_uniform(&world->ao_env_energy), &f);
|
|
GPU_link(mat, "shade_maddf", shr->combined, f, shi->rgb, &shr->combined);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ambient color */
|
|
if (GPU_link_changed(shi->amb) || ma->amb != 0.0f) {
|
|
GPU_link(mat, "shade_maddf", shr->combined, GPU_uniform(&ma->amb),
|
|
GPU_dynamic_uniform(GPUWorld.ambcol, GPU_DYNAMIC_AMBIENT_COLOR, NULL),
|
|
&shr->combined);
|
|
}
|
|
}
|
|
|
|
if (ma->mode & MA_TRANSP && (ma->mode & (MA_ZTRANSP | MA_RAYTRANSP))) {
|
|
if (GPU_link_changed(shi->spectra) || ma->spectra != 0.0f) {
|
|
GPU_link(mat, "alpha_spec_correction", shr->spec, shi->spectra,
|
|
shi->alpha, &shr->alpha);
|
|
}
|
|
}
|
|
|
|
if (ma->mode & MA_RAMP_COL) ramp_diffuse_result(shi, &shr->combined);
|
|
if (ma->mode & MA_RAMP_SPEC) ramp_spec_result(shi, &shr->spec);
|
|
|
|
if (GPU_link_changed(shi->refcol))
|
|
GPU_link(mat, "shade_add_mirror", shi->mir, shi->refcol, shr->combined, &shr->combined);
|
|
|
|
if (GPU_link_changed(shi->spec) || ma->spec != 0.0f)
|
|
GPU_link(mat, "shade_add", shr->combined, shr->spec, &shr->combined);
|
|
}
|
|
|
|
GPU_link(mat, "mtex_alpha_to_col", shr->combined, shr->alpha, &shr->combined);
|
|
|
|
if (ma->shade_flag & MA_OBCOLOR)
|
|
GPU_link(mat, "shade_obcolor", shr->combined, GPU_builtin(GPU_OBCOLOR), &shr->combined);
|
|
|
|
if (!(ma->mode & MA_NOMIST)) {
|
|
GPU_link(mat, "shade_mist_factor", GPU_builtin(GPU_VIEW_POSITION),
|
|
GPU_dynamic_uniform(&GPUWorld.mistenabled, GPU_DYNAMIC_MIST_ENABLE, NULL),
|
|
GPU_dynamic_uniform(&GPUWorld.miststart, GPU_DYNAMIC_MIST_START, NULL),
|
|
GPU_dynamic_uniform(&GPUWorld.mistdistance, GPU_DYNAMIC_MIST_DISTANCE, NULL),
|
|
GPU_dynamic_uniform(&GPUWorld.mistype, GPU_DYNAMIC_MIST_TYPE, NULL),
|
|
GPU_dynamic_uniform(&GPUWorld.mistintensity, GPU_DYNAMIC_MIST_INTENSITY, NULL), &mistfac);
|
|
|
|
GPU_link(mat, "mix_blend", mistfac, shr->combined,
|
|
GPU_dynamic_uniform(GPUWorld.mistcol, GPU_DYNAMIC_MIST_COLOR, NULL), &shr->combined);
|
|
}
|
|
|
|
if (!mat->alpha) {
|
|
if (world && (GPU_link_changed(shr->alpha) || ma->alpha != 1.0f))
|
|
GPU_link(mat, "shade_world_mix", GPU_dynamic_uniform(GPUWorld.horicol, GPU_DYNAMIC_HORIZON_COLOR, NULL),
|
|
shr->combined, &shr->combined);
|
|
|
|
GPU_link(mat, "shade_alpha_opaque", shr->combined, &shr->combined);
|
|
}
|
|
|
|
if (ma->shade_flag & MA_OBCOLOR) {
|
|
mat->obcolalpha = 1;
|
|
GPU_link(mat, "shade_alpha_obcolor", shr->combined, GPU_builtin(GPU_OBCOLOR), &shr->combined);
|
|
}
|
|
}
|
|
|
|
static GPUNodeLink *GPU_blender_material(GPUMaterial *mat, Material *ma)
|
|
{
|
|
GPUShadeInput shi;
|
|
GPUShadeResult shr;
|
|
|
|
GPU_shadeinput_set(mat, ma, &shi);
|
|
GPU_shaderesult_set(&shi, &shr);
|
|
|
|
return shr.combined;
|
|
}
|
|
|
|
static GPUNodeLink *gpu_material_diffuse_bsdf(GPUMaterial *mat, Material *ma)
|
|
{
|
|
static float roughness = 0.0f;
|
|
GPUNodeLink *outlink;
|
|
|
|
GPU_link(mat, "node_bsdf_diffuse",
|
|
GPU_uniform(&ma->r), GPU_uniform(&roughness), GPU_builtin(GPU_VIEW_NORMAL), &outlink);
|
|
|
|
return outlink;
|
|
}
|
|
|
|
static GPUNodeLink *gpu_material_preview_matcap(GPUMaterial *mat, Material *ma)
|
|
{
|
|
GPUNodeLink *outlink;
|
|
|
|
/* some explanations here:
|
|
* matcap normal holds the normal remapped to the 0.0 - 1.0 range. To take advantage of flat shading, we abuse
|
|
* the built in secondary color of opengl. Color is just the regular color, which should include mask value too.
|
|
* This also needs flat shading so we use the primary opengl color built-in */
|
|
GPU_link(mat, "material_preview_matcap", GPU_uniform(&ma->r), GPU_image_preview(ma->preview),
|
|
GPU_opengl_builtin(GPU_MATCAP_NORMAL), GPU_opengl_builtin(GPU_COLOR), &outlink);
|
|
|
|
return outlink;
|
|
}
|
|
|
|
/* new solid draw mode with glsl matcaps */
|
|
GPUMaterial *GPU_material_matcap(Scene *scene, Material *ma, bool use_opensubdiv)
|
|
{
|
|
GPUMaterial *mat;
|
|
GPUNodeLink *outlink;
|
|
LinkData *link;
|
|
|
|
for (link = ma->gpumaterial.first; link; link = link->next) {
|
|
GPUMaterial *current_material = (GPUMaterial *)link->data;
|
|
if (current_material->scene == scene &&
|
|
current_material->is_opensubdiv == use_opensubdiv)
|
|
{
|
|
return current_material;
|
|
}
|
|
}
|
|
|
|
/* allocate material */
|
|
mat = GPU_material_construct_begin(ma);
|
|
mat->scene = scene;
|
|
mat->type = GPU_MATERIAL_TYPE_MESH;
|
|
mat->is_opensubdiv = use_opensubdiv;
|
|
|
|
if (ma->preview && ma->preview->rect[0]) {
|
|
outlink = gpu_material_preview_matcap(mat, ma);
|
|
}
|
|
else {
|
|
outlink = gpu_material_diffuse_bsdf(mat, ma);
|
|
}
|
|
|
|
GPU_material_output_link(mat, outlink);
|
|
|
|
gpu_material_construct_end(mat, "matcap_pass");
|
|
|
|
/* note that even if building the shader fails in some way, we still keep
|
|
* it to avoid trying to compile again and again, and simple do not use
|
|
* the actual shader on drawing */
|
|
|
|
link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink");
|
|
link->data = mat;
|
|
BLI_addtail(&ma->gpumaterial, link);
|
|
|
|
return mat;
|
|
}
|
|
|
|
static void do_world_tex(GPUShadeInput *shi, struct World *wo, GPUNodeLink **hor, GPUNodeLink **zen, GPUNodeLink **blend)
|
|
{
|
|
GPUMaterial *mat = shi->gpumat;
|
|
GPUNodeLink *texco, *tin, *trgb, *stencil, *tcol, *zenfac;
|
|
MTex *mtex;
|
|
Tex *tex;
|
|
float ofs[3], zero = 0.0f;
|
|
int tex_nr, rgbnor;
|
|
|
|
GPU_link(mat, "set_value_one", &stencil);
|
|
/* go over texture slots */
|
|
for (tex_nr = 0; tex_nr < MAX_MTEX; tex_nr++) {
|
|
if (wo->mtex[tex_nr]) {
|
|
mtex = wo->mtex[tex_nr];
|
|
tex = mtex->tex;
|
|
if (tex == NULL || !tex->ima || (tex->type != TEX_IMAGE && tex->type != TEX_ENVMAP))
|
|
continue;
|
|
/* which coords */
|
|
if (mtex->texco == TEXCO_VIEW || mtex->texco == TEXCO_GLOB) {
|
|
if (tex->type == TEX_IMAGE)
|
|
texco = GPU_builtin(GPU_VIEW_POSITION);
|
|
else if (tex->type == TEX_ENVMAP)
|
|
GPU_link(mat, "background_transform_to_world", GPU_builtin(GPU_VIEW_POSITION), &texco);
|
|
}
|
|
else if (mtex->texco == TEXCO_EQUIRECTMAP || mtex->texco == TEXCO_ANGMAP) {
|
|
if ((tex->type == TEX_IMAGE && wo->skytype & WO_SKYREAL) || tex->type == TEX_ENVMAP)
|
|
GPU_link(mat, "background_transform_to_world", GPU_builtin(GPU_VIEW_POSITION), &texco);
|
|
else
|
|
texco = GPU_builtin(GPU_VIEW_POSITION);
|
|
}
|
|
else
|
|
continue;
|
|
GPU_link(mat, "texco_norm", texco, &texco);
|
|
if (tex->type == TEX_IMAGE && !(wo->skytype & WO_SKYREAL)) {
|
|
GPU_link(mat, "mtex_2d_mapping", texco, &texco);
|
|
}
|
|
if (mtex->size[0] != 1.0f || mtex->size[1] != 1.0f || mtex->size[2] != 1.0f) {
|
|
float size[3] = { mtex->size[0], mtex->size[1], mtex->size[2] };
|
|
if (tex->type == TEX_ENVMAP) {
|
|
size[1] = mtex->size[2];
|
|
size[2] = mtex->size[1];
|
|
}
|
|
GPU_link(mat, "mtex_mapping_size", texco, GPU_uniform(size), &texco);
|
|
}
|
|
ofs[0] = mtex->ofs[0] + 0.5f - 0.5f * mtex->size[0];
|
|
if (tex->type == TEX_ENVMAP) {
|
|
ofs[1] = -mtex->ofs[2] + 0.5f - 0.5f * mtex->size[2];
|
|
ofs[2] = mtex->ofs[1] + 0.5f - 0.5f * mtex->size[1];
|
|
}
|
|
else {
|
|
ofs[1] = mtex->ofs[1] + 0.5f - 0.5f * mtex->size[1];
|
|
ofs[2] = 0.0;
|
|
}
|
|
if (ofs[0] != 0.0f || ofs[1] != 0.0f || ofs[2] != 0.0f)
|
|
GPU_link(mat, "mtex_mapping_ofs", texco, GPU_uniform(ofs), &texco);
|
|
if (mtex->texco == TEXCO_EQUIRECTMAP) {
|
|
GPU_link(mat, "node_tex_environment_equirectangular", texco, GPU_image(tex->ima, &tex->iuser, false), &trgb);
|
|
}
|
|
else if (mtex->texco == TEXCO_ANGMAP) {
|
|
GPU_link(mat, "node_tex_environment_mirror_ball", texco, GPU_image(tex->ima, &tex->iuser, false), &trgb);
|
|
}
|
|
else {
|
|
if (tex->type == TEX_ENVMAP)
|
|
GPU_link(mat, "mtex_cube_map", texco, GPU_cube_map(tex->ima, &tex->iuser, false), &tin, &trgb);
|
|
else if (tex->type == TEX_IMAGE)
|
|
GPU_link(mat, "mtex_image", texco, GPU_image(tex->ima, &tex->iuser, false), &tin, &trgb);
|
|
}
|
|
rgbnor = TEX_RGB;
|
|
if (tex->type == TEX_IMAGE || tex->type == TEX_ENVMAP)
|
|
if (GPU_material_do_color_management(mat))
|
|
GPU_link(mat, "srgb_to_linearrgb", trgb, &trgb);
|
|
/* texture output */
|
|
if ((rgbnor & TEX_RGB) && (mtex->texflag & MTEX_RGBTOINT)) {
|
|
GPU_link(mat, "mtex_rgbtoint", trgb, &tin);
|
|
rgbnor -= TEX_RGB;
|
|
}
|
|
if (mtex->texflag & MTEX_NEGATIVE) {
|
|
if (rgbnor & TEX_RGB)
|
|
GPU_link(mat, "mtex_rgb_invert", trgb, &trgb);
|
|
else
|
|
GPU_link(mat, "mtex_value_invert", tin, &tin);
|
|
}
|
|
if (mtex->texflag & MTEX_STENCIL) {
|
|
if (rgbnor & TEX_RGB)
|
|
GPU_link(mat, "mtex_rgb_stencil", stencil, trgb, &stencil, &trgb);
|
|
else
|
|
GPU_link(mat, "mtex_value_stencil", stencil, tin, &stencil, &tin);
|
|
}
|
|
else {
|
|
if (rgbnor & TEX_RGB)
|
|
GPU_link(mat, "mtex_alpha_multiply_value", trgb, stencil, &trgb);
|
|
else
|
|
GPU_link(mat, "math_multiply", stencil, tin, &tin);
|
|
}
|
|
/* color mapping */
|
|
if (mtex->mapto & (WOMAP_HORIZ + WOMAP_ZENUP + WOMAP_ZENDOWN)) {
|
|
if ((rgbnor & TEX_RGB) == 0)
|
|
GPU_link(mat, "set_rgb", GPU_uniform(&mtex->r), &trgb);
|
|
else
|
|
GPU_link(mat, "mtex_alpha_from_col", trgb, &tin);
|
|
GPU_link(mat, "set_rgb", trgb, &tcol);
|
|
if (mtex->mapto & WOMAP_HORIZ) {
|
|
texture_rgb_blend(mat, tcol, *hor, tin, GPU_uniform(&mtex->colfac), mtex->blendtype, hor);
|
|
}
|
|
if (mtex->mapto & (WOMAP_ZENUP + WOMAP_ZENDOWN)) {
|
|
GPU_link(mat, "set_value_zero", &zenfac);
|
|
if (wo->skytype & WO_SKYREAL) {
|
|
if (mtex->mapto & WOMAP_ZENUP) {
|
|
if (mtex->mapto & WOMAP_ZENDOWN) {
|
|
GPU_link(mat, "world_zen_mapping", shi->view, GPU_uniform(&mtex->zenupfac),
|
|
GPU_uniform(&mtex->zendownfac), &zenfac);
|
|
}
|
|
else {
|
|
GPU_link(mat, "world_zen_mapping", shi->view, GPU_uniform(&mtex->zenupfac),
|
|
GPU_uniform(&zero), &zenfac);
|
|
}
|
|
}
|
|
else if (mtex->mapto & WOMAP_ZENDOWN) {
|
|
GPU_link(mat, "world_zen_mapping", shi->view, GPU_uniform(&zero),
|
|
GPU_uniform(&mtex->zendownfac), &zenfac);
|
|
}
|
|
}
|
|
else {
|
|
if (mtex->mapto & WOMAP_ZENUP)
|
|
GPU_link(mat, "set_value", GPU_uniform(&mtex->zenupfac), &zenfac);
|
|
else if (mtex->mapto & WOMAP_ZENDOWN)
|
|
GPU_link(mat, "set_value", GPU_uniform(&mtex->zendownfac), &zenfac);
|
|
}
|
|
texture_rgb_blend(mat, tcol, *zen, tin, zenfac, mtex->blendtype, zen);
|
|
}
|
|
}
|
|
if (mtex->mapto & WOMAP_BLEND && wo->skytype & WO_SKYBLEND) {
|
|
if (rgbnor & TEX_RGB)
|
|
GPU_link(mat, "mtex_rgbtoint", trgb, &tin);
|
|
texture_value_blend(mat, GPU_uniform(&mtex->def_var), *blend, tin, GPU_uniform(&mtex->blendfac), mtex->blendtype, blend);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void gpu_material_old_world(struct GPUMaterial *mat, struct World *wo)
|
|
{
|
|
GPUShadeInput shi;
|
|
GPUShadeResult shr;
|
|
GPUNodeLink *hor, *zen, *ray, *blend;
|
|
|
|
shi.gpumat = mat;
|
|
|
|
for (int i = 0; i < MAX_MTEX; i++) {
|
|
if (wo->mtex[i] && wo->mtex[i]->tex) {
|
|
wo->skytype |= WO_SKYTEX;
|
|
break;
|
|
}
|
|
}
|
|
if ((wo->skytype & (WO_SKYBLEND + WO_SKYTEX)) == 0) {
|
|
GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&wo->horr, GPU_DYNAMIC_HORIZON_COLOR, NULL), &shr.combined);
|
|
}
|
|
else {
|
|
GPU_link(mat, "set_rgb_zero", &shi.rgb);
|
|
GPU_link(mat, "background_transform_to_world", GPU_builtin(GPU_VIEW_POSITION), &ray);
|
|
if (wo->skytype & WO_SKYPAPER)
|
|
GPU_link(mat, "world_paper_view", GPU_builtin(GPU_VIEW_POSITION), &shi.view);
|
|
else
|
|
GPU_link(mat, "shade_view", ray, &shi.view);
|
|
if (wo->skytype & WO_SKYBLEND) {
|
|
if (wo->skytype & WO_SKYPAPER) {
|
|
if (wo->skytype & WO_SKYREAL)
|
|
GPU_link(mat, "world_blend_paper_real", GPU_builtin(GPU_VIEW_POSITION), &blend);
|
|
else
|
|
GPU_link(mat, "world_blend_paper", GPU_builtin(GPU_VIEW_POSITION), &blend);
|
|
}
|
|
else {
|
|
if (wo->skytype & WO_SKYREAL)
|
|
GPU_link(mat, "world_blend_real", ray, &blend);
|
|
else
|
|
GPU_link(mat, "world_blend", ray, &blend);
|
|
}
|
|
}
|
|
else {
|
|
GPU_link(mat, "set_value_zero", &blend);
|
|
}
|
|
GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&wo->horr, GPU_DYNAMIC_HORIZON_COLOR, NULL), &hor);
|
|
GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&wo->zenr, GPU_DYNAMIC_ZENITH_COLOR, NULL), &zen);
|
|
do_world_tex(&shi, wo, &hor, &zen, &blend);
|
|
if (wo->skytype & WO_SKYBLEND)
|
|
GPU_link(mat, "node_mix_shader", blend, hor, zen, &shi.rgb);
|
|
else
|
|
GPU_link(mat, "set_rgb", hor, &shi.rgb);
|
|
GPU_link(mat, "set_rgb", shi.rgb, &shr.combined);
|
|
}
|
|
GPU_material_output_link(mat, shr.combined);
|
|
}
|
|
|
|
GPUMaterial *GPU_material_world(struct Scene *scene, struct World *wo)
|
|
{
|
|
LinkData *link;
|
|
GPUMaterial *mat;
|
|
|
|
for (link = wo->gpumaterial.first; link; link = link->next)
|
|
if (((GPUMaterial *)link->data)->scene == scene)
|
|
return link->data;
|
|
|
|
/* allocate material */
|
|
mat = GPU_material_construct_begin(NULL);
|
|
mat->scene = scene;
|
|
mat->type = GPU_MATERIAL_TYPE_WORLD;
|
|
|
|
/* create nodes */
|
|
if (BKE_scene_use_new_shading_nodes(scene) && wo->nodetree && wo->use_nodes) {
|
|
ntreeGPUMaterialNodes(wo->nodetree, mat, NODE_NEW_SHADING);
|
|
}
|
|
else {
|
|
gpu_material_old_world(mat, wo);
|
|
}
|
|
|
|
if (GPU_material_do_color_management(mat))
|
|
if (mat->outlink)
|
|
GPU_link(mat, "linearrgb_to_srgb", mat->outlink, &mat->outlink);
|
|
|
|
gpu_material_construct_end(mat, wo->id.name);
|
|
|
|
/* note that even if building the shader fails in some way, we still keep
|
|
* it to avoid trying to compile again and again, and simple do not use
|
|
* the actual shader on drawing */
|
|
|
|
link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink");
|
|
link->data = mat;
|
|
BLI_addtail(&wo->gpumaterial, link);
|
|
|
|
return mat;
|
|
}
|
|
|
|
GPUMaterial *GPU_material_from_nodetree_find(
|
|
ListBase *gpumaterials, const void *engine_type, int options)
|
|
{
|
|
for (LinkData *link = gpumaterials->first; link; link = link->next) {
|
|
GPUMaterial *current_material = (GPUMaterial *)link->data;
|
|
if (current_material->engine_type == engine_type &&
|
|
current_material->options == options)
|
|
{
|
|
return current_material;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* TODO: This is supposed to replace GPU_material_from_blender/_world in the future
|
|
*
|
|
* \note Caller must use #GPU_material_from_nodetree_find to re-use existing materials,
|
|
* This is enforced since constructing other arguments to this function may be expensive
|
|
* so only do this when they are needed.
|
|
*/
|
|
GPUMaterial *GPU_material_from_nodetree(
|
|
Scene *scene, struct bNodeTree *ntree, ListBase *gpumaterials, const void *engine_type, int options,
|
|
const char *vert_code, const char *geom_code, const char *frag_lib, const char *defines, bool deferred)
|
|
{
|
|
LinkData *link;
|
|
bool has_volume_output, has_surface_output;
|
|
|
|
/* Caller must re-use materials. */
|
|
BLI_assert(GPU_material_from_nodetree_find(gpumaterials, engine_type, options) == NULL);
|
|
|
|
/* allocate material */
|
|
GPUMaterial *mat = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial");;
|
|
mat->scene = scene;
|
|
mat->engine_type = engine_type;
|
|
mat->options = options;
|
|
|
|
ntreeGPUMaterialNodes(ntree, mat, NODE_NEW_SHADING | NODE_NEWER_SHADING);
|
|
ntreeGPUMaterialDomain(ntree, &has_surface_output, &has_volume_output);
|
|
|
|
if (has_surface_output) {
|
|
mat->domain |= GPU_DOMAIN_SURFACE;
|
|
}
|
|
if (has_volume_output) {
|
|
mat->domain |= GPU_DOMAIN_VOLUME;
|
|
}
|
|
|
|
if (!deferred) {
|
|
GPU_material_generate_pass(mat, vert_code, geom_code, frag_lib, defines);
|
|
}
|
|
else if (mat->outlink) {
|
|
/* Prune the unused nodes and extract attribs before compiling so the
|
|
* generated VBOs are ready to accept the future shader. */
|
|
GPU_nodes_prune(&mat->nodes, mat->outlink);
|
|
GPU_nodes_get_vertex_attributes(&mat->nodes, &mat->attribs);
|
|
mat->status = GPU_MAT_QUEUED;
|
|
}
|
|
|
|
/* note that even if building the shader fails in some way, we still keep
|
|
* it to avoid trying to compile again and again, and simple do not use
|
|
* the actual shader on drawing */
|
|
|
|
link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink");
|
|
link->data = mat;
|
|
BLI_addtail(gpumaterials, link);
|
|
|
|
return mat;
|
|
}
|
|
|
|
/* Calls this function if /a mat was created with deferred compilation. */
|
|
void GPU_material_generate_pass(
|
|
GPUMaterial *mat, const char *vert_code, const char *geom_code, const char *frag_lib, const char *defines)
|
|
{
|
|
BLI_assert(mat->pass == NULL); /* Only run once! */
|
|
if (mat->outlink) {
|
|
mat->pass = GPU_generate_pass_new(
|
|
mat, mat->outlink, &mat->attribs, &mat->nodes, &mat->inputs, vert_code, geom_code, frag_lib, defines);
|
|
mat->status = (mat->pass) ? GPU_MAT_SUCCESS : GPU_MAT_FAILED;
|
|
}
|
|
}
|
|
|
|
GPUMaterial *GPU_material_from_blender(Scene *scene, Material *ma, bool use_opensubdiv)
|
|
{
|
|
GPUMaterial *mat;
|
|
GPUNodeLink *outlink;
|
|
LinkData *link;
|
|
|
|
for (link = ma->gpumaterial.first; link; link = link->next) {
|
|
GPUMaterial *current_material = (GPUMaterial *)link->data;
|
|
if (current_material->scene == scene &&
|
|
current_material->is_opensubdiv == use_opensubdiv)
|
|
{
|
|
return current_material;
|
|
}
|
|
}
|
|
|
|
/* allocate material */
|
|
mat = GPU_material_construct_begin(ma);
|
|
mat->scene = scene;
|
|
mat->type = GPU_MATERIAL_TYPE_MESH;
|
|
mat->is_opensubdiv = use_opensubdiv;
|
|
|
|
/* render pipeline option */
|
|
bool new_shading_nodes = BKE_scene_use_new_shading_nodes(scene);
|
|
if (!new_shading_nodes && (ma->mode & MA_TRANSP))
|
|
GPU_material_enable_alpha(mat);
|
|
else if (new_shading_nodes && ma->alpha < 1.0f)
|
|
GPU_material_enable_alpha(mat);
|
|
|
|
if (!(scene->gm.flag & GAME_GLSL_NO_NODES) && ma->nodetree && ma->use_nodes) {
|
|
/* create nodes */
|
|
if (new_shading_nodes)
|
|
ntreeGPUMaterialNodes(ma->nodetree, mat, NODE_NEW_SHADING);
|
|
else
|
|
ntreeGPUMaterialNodes(ma->nodetree, mat, NODE_OLD_SHADING);
|
|
}
|
|
else {
|
|
if (new_shading_nodes) {
|
|
/* create simple diffuse material instead of nodes */
|
|
outlink = gpu_material_diffuse_bsdf(mat, ma);
|
|
}
|
|
else {
|
|
/* create blender material */
|
|
outlink = GPU_blender_material(mat, ma);
|
|
}
|
|
|
|
GPU_material_output_link(mat, outlink);
|
|
}
|
|
|
|
if (GPU_material_do_color_management(mat))
|
|
if (mat->outlink)
|
|
GPU_link(mat, "linearrgb_to_srgb", mat->outlink, &mat->outlink);
|
|
|
|
gpu_material_construct_end(mat, ma->id.name);
|
|
|
|
/* note that even if building the shader fails in some way, we still keep
|
|
* it to avoid trying to compile again and again, and simple do not use
|
|
* the actual shader on drawing */
|
|
|
|
link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink");
|
|
link->data = mat;
|
|
BLI_addtail(&ma->gpumaterial, link);
|
|
|
|
return mat;
|
|
}
|
|
|
|
void GPU_materials_free(void)
|
|
{
|
|
Object *ob;
|
|
Material *ma;
|
|
World *wo;
|
|
extern Material defmaterial;
|
|
|
|
for (ma = G.main->mat.first; ma; ma = ma->id.next)
|
|
GPU_material_free(&ma->gpumaterial);
|
|
|
|
for (wo = G.main->world.first; wo; wo = wo->id.next)
|
|
GPU_material_free(&wo->gpumaterial);
|
|
|
|
GPU_material_free(&defmaterial.gpumaterial);
|
|
|
|
for (ob = G.main->object.first; ob; ob = ob->id.next)
|
|
GPU_lamp_free(ob);
|
|
}
|
|
|
|
/* Lamps and shadow buffers */
|
|
|
|
GPUNodeLink *GPU_lamp_get_data(
|
|
GPUMaterial *mat, GPULamp *lamp,
|
|
GPUNodeLink **r_col, GPUNodeLink **r_lv, GPUNodeLink **r_dist, GPUNodeLink **r_shadow, GPUNodeLink **r_energy)
|
|
{
|
|
GPUNodeLink *visifac;
|
|
|
|
*r_col = GPU_dynamic_uniform(lamp->dyncol, GPU_DYNAMIC_LAMP_DYNCOL, lamp->ob);
|
|
*r_energy = GPU_dynamic_uniform(&lamp->dynenergy, GPU_DYNAMIC_LAMP_DYNENERGY, lamp->ob);
|
|
visifac = lamp_get_visibility(mat, lamp, r_lv, r_dist);
|
|
|
|
shade_light_textures(mat, lamp, r_col);
|
|
|
|
if (GPU_lamp_has_shadow_buffer(lamp)) {
|
|
GPUNodeLink *vn, *inp;
|
|
|
|
GPU_link(mat, "shade_norm", GPU_builtin(GPU_VIEW_NORMAL), &vn);
|
|
GPU_link(mat, "shade_inp", vn, *r_lv, &inp);
|
|
mat->dynproperty |= DYN_LAMP_PERSMAT;
|
|
|
|
if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) {
|
|
GPU_link(mat, "shadows_only_vsm",
|
|
GPU_builtin(GPU_VIEW_POSITION),
|
|
GPU_dynamic_texture(lamp->tex, GPU_DYNAMIC_SAMPLER_2DSHADOW, lamp->ob),
|
|
GPU_dynamic_uniform((float *)lamp->dynpersmat, GPU_DYNAMIC_LAMP_DYNPERSMAT, lamp->ob),
|
|
GPU_uniform(&lamp->bias), GPU_uniform(&lamp->la->bleedbias),
|
|
GPU_uniform(lamp->shadow_color), inp, r_shadow);
|
|
}
|
|
else {
|
|
GPU_link(mat, "shadows_only",
|
|
GPU_builtin(GPU_VIEW_POSITION),
|
|
GPU_dynamic_texture(lamp->tex, GPU_DYNAMIC_SAMPLER_2DSHADOW, lamp->ob),
|
|
GPU_dynamic_uniform((float *)lamp->dynpersmat, GPU_DYNAMIC_LAMP_DYNPERSMAT, lamp->ob),
|
|
GPU_uniform(&lamp->bias), GPU_uniform(lamp->shadow_color), inp, r_shadow);
|
|
}
|
|
}
|
|
else {
|
|
GPU_link(mat, "set_rgb_one", r_shadow);
|
|
}
|
|
|
|
/* ensure shadow buffer and lamp textures will be updated */
|
|
add_user_list(&mat->lamps, lamp);
|
|
|
|
return visifac;
|
|
}
|
|
|
|
/* export the GLSL shader */
|
|
|
|
GPUShaderExport *GPU_shader_export(struct Scene *scene, struct Material *ma)
|
|
{
|
|
static struct {
|
|
GPUBuiltin gputype;
|
|
GPUDynamicType dynamictype;
|
|
GPUDataType datatype;
|
|
} builtins[] = {
|
|
{ GPU_VIEW_MATRIX, GPU_DYNAMIC_OBJECT_VIEWMAT, GPU_DATA_16F },
|
|
{ GPU_INVERSE_VIEW_MATRIX, GPU_DYNAMIC_OBJECT_VIEWIMAT, GPU_DATA_16F },
|
|
{ GPU_OBJECT_MATRIX, GPU_DYNAMIC_OBJECT_MAT, GPU_DATA_16F },
|
|
{ GPU_INVERSE_OBJECT_MATRIX, GPU_DYNAMIC_OBJECT_IMAT, GPU_DATA_16F },
|
|
{ GPU_LOC_TO_VIEW_MATRIX, GPU_DYNAMIC_OBJECT_LOCTOVIEWMAT, GPU_DATA_16F },
|
|
{ GPU_INVERSE_LOC_TO_VIEW_MATRIX, GPU_DYNAMIC_OBJECT_LOCTOVIEWIMAT, GPU_DATA_16F },
|
|
{ GPU_OBCOLOR, GPU_DYNAMIC_OBJECT_COLOR, GPU_DATA_4F },
|
|
{ GPU_AUTO_BUMPSCALE, GPU_DYNAMIC_OBJECT_AUTOBUMPSCALE, GPU_DATA_1F },
|
|
{ 0 }
|
|
};
|
|
|
|
GPUShaderExport *shader = NULL;
|
|
GPUInput *input;
|
|
int liblen, fraglen;
|
|
|
|
/* TODO(sergey): How to determine whether we need OSD or not here? */
|
|
GPUMaterial *mat = GPU_material_from_blender(scene, ma, false);
|
|
GPUPass *pass = (mat) ? mat->pass : NULL;
|
|
|
|
if (pass && pass->fragmentcode && pass->vertexcode) {
|
|
shader = MEM_callocN(sizeof(GPUShaderExport), "GPUShaderExport");
|
|
|
|
for (input = mat->inputs.first; input; input = input->next) {
|
|
GPUInputUniform *uniform = MEM_callocN(sizeof(GPUInputUniform), "GPUInputUniform");
|
|
|
|
if (input->ima) {
|
|
/* image sampler uniform */
|
|
uniform->type = GPU_DYNAMIC_SAMPLER_2DIMAGE;
|
|
uniform->datatype = GPU_DATA_1I;
|
|
uniform->image = input->ima;
|
|
uniform->texnumber = input->texid;
|
|
BLI_strncpy(uniform->varname, input->shadername, sizeof(uniform->varname));
|
|
}
|
|
else if (input->tex) {
|
|
/* generated buffer */
|
|
uniform->texnumber = input->texid;
|
|
uniform->datatype = GPU_DATA_1I;
|
|
BLI_strncpy(uniform->varname, input->shadername, sizeof(uniform->varname));
|
|
|
|
switch (input->textype) {
|
|
case GPU_SHADOW2D:
|
|
uniform->type = GPU_DYNAMIC_SAMPLER_2DSHADOW;
|
|
uniform->lamp = input->dynamicdata;
|
|
break;
|
|
case GPU_TEX2D:
|
|
if (GPU_texture_opengl_bindcode(input->tex)) {
|
|
uniform->type = GPU_DYNAMIC_SAMPLER_2DBUFFER;
|
|
glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(input->tex));
|
|
uniform->texsize = GPU_texture_width(input->tex) * GPU_texture_height(input->tex);
|
|
uniform->texpixels = MEM_mallocN(uniform->texsize * 4, "RGBApixels");
|
|
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, uniform->texpixels);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
break;
|
|
|
|
case GPU_NONE:
|
|
case GPU_TEX3D:
|
|
case GPU_TEXCUBE:
|
|
case GPU_FLOAT:
|
|
case GPU_VEC2:
|
|
case GPU_VEC3:
|
|
case GPU_VEC4:
|
|
case GPU_MAT3:
|
|
case GPU_MAT4:
|
|
case GPU_CLOSURE:
|
|
case GPU_ATTRIB:
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
uniform->type = input->dynamictype;
|
|
BLI_strncpy(uniform->varname, input->shadername, sizeof(uniform->varname));
|
|
switch (input->type) {
|
|
case GPU_FLOAT:
|
|
uniform->datatype = GPU_DATA_1F;
|
|
break;
|
|
case GPU_VEC2:
|
|
uniform->datatype = GPU_DATA_2F;
|
|
break;
|
|
case GPU_VEC3:
|
|
uniform->datatype = GPU_DATA_3F;
|
|
break;
|
|
case GPU_VEC4:
|
|
uniform->datatype = GPU_DATA_4F;
|
|
break;
|
|
case GPU_MAT3:
|
|
uniform->datatype = GPU_DATA_9F;
|
|
break;
|
|
case GPU_MAT4:
|
|
uniform->datatype = GPU_DATA_16F;
|
|
break;
|
|
|
|
case GPU_NONE:
|
|
case GPU_CLOSURE:
|
|
case GPU_TEX2D:
|
|
case GPU_TEX3D:
|
|
case GPU_TEXCUBE:
|
|
case GPU_SHADOW2D:
|
|
case GPU_ATTRIB:
|
|
break;
|
|
}
|
|
|
|
if (GPU_DYNAMIC_GROUP_FROM_TYPE(uniform->type) == GPU_DYNAMIC_GROUP_LAMP)
|
|
uniform->lamp = input->dynamicdata;
|
|
|
|
if (GPU_DYNAMIC_GROUP_FROM_TYPE(uniform->type) == GPU_DYNAMIC_GROUP_MAT)
|
|
uniform->material = input->dynamicdata;
|
|
}
|
|
|
|
if (uniform->type != GPU_DYNAMIC_NONE)
|
|
BLI_addtail(&shader->uniforms, uniform);
|
|
else
|
|
MEM_freeN(uniform);
|
|
}
|
|
|
|
/* process builtin uniform */
|
|
for (int i = 0; builtins[i].gputype; i++) {
|
|
if (mat->builtins & builtins[i].gputype) {
|
|
GPUInputUniform *uniform = MEM_callocN(sizeof(GPUInputUniform), "GPUInputUniform");
|
|
uniform->type = builtins[i].dynamictype;
|
|
uniform->datatype = builtins[i].datatype;
|
|
BLI_strncpy(uniform->varname, GPU_builtin_name(builtins[i].gputype), sizeof(uniform->varname));
|
|
BLI_addtail(&shader->uniforms, uniform);
|
|
}
|
|
}
|
|
|
|
/* now link fragment shader with library shader */
|
|
/* TBD: remove the function that are not used in the main function */
|
|
liblen = (pass->libcode) ? strlen(pass->libcode) : 0;
|
|
fraglen = strlen(pass->fragmentcode);
|
|
shader->fragment = (char *)MEM_mallocN(liblen + fraglen + 1, "GPUFragShader");
|
|
if (pass->libcode)
|
|
memcpy(shader->fragment, pass->libcode, liblen);
|
|
memcpy(&shader->fragment[liblen], pass->fragmentcode, fraglen);
|
|
shader->fragment[liblen + fraglen] = 0;
|
|
|
|
// export the attribute
|
|
for (int i = 0; i < mat->attribs.totlayer; i++) {
|
|
GPUInputAttribute *attribute = MEM_callocN(sizeof(GPUInputAttribute), "GPUInputAttribute");
|
|
attribute->type = mat->attribs.layer[i].type;
|
|
attribute->number = mat->attribs.layer[i].glindex;
|
|
BLI_snprintf(attribute->varname, sizeof(attribute->varname), "att%d", mat->attribs.layer[i].attribid);
|
|
|
|
switch (attribute->type) {
|
|
case CD_TANGENT:
|
|
attribute->datatype = GPU_DATA_4F;
|
|
break;
|
|
case CD_MTFACE:
|
|
attribute->datatype = GPU_DATA_2F;
|
|
attribute->name = mat->attribs.layer[i].name;
|
|
break;
|
|
case CD_MCOL:
|
|
attribute->datatype = GPU_DATA_4UB;
|
|
attribute->name = mat->attribs.layer[i].name;
|
|
break;
|
|
case CD_ORCO:
|
|
attribute->datatype = GPU_DATA_3F;
|
|
break;
|
|
}
|
|
|
|
if (attribute->datatype != GPU_DATA_NONE)
|
|
BLI_addtail(&shader->attributes, attribute);
|
|
else
|
|
MEM_freeN(attribute);
|
|
}
|
|
|
|
/* export the vertex shader */
|
|
shader->vertex = BLI_strdup(pass->vertexcode);
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
void GPU_free_shader_export(GPUShaderExport *shader)
|
|
{
|
|
if (shader == NULL)
|
|
return;
|
|
|
|
for (GPUInputUniform *uniform = shader->uniforms.first; uniform; uniform = uniform->next)
|
|
if (uniform->texpixels)
|
|
MEM_freeN(uniform->texpixels);
|
|
|
|
BLI_freelistN(&shader->uniforms);
|
|
BLI_freelistN(&shader->attributes);
|
|
|
|
if (shader->vertex)
|
|
MEM_freeN(shader->vertex);
|
|
if (shader->fragment)
|
|
MEM_freeN(shader->fragment);
|
|
|
|
MEM_freeN(shader);
|
|
}
|
|
|
|
#ifdef WITH_OPENSUBDIV
|
|
void GPU_material_update_fvar_offset(GPUMaterial *gpu_material,
|
|
DerivedMesh *dm)
|
|
{
|
|
GPUPass *pass = gpu_material->pass;
|
|
GPUShader *shader = (pass != NULL ? pass->shader : NULL);
|
|
ListBase *inputs = (pass != NULL ? &gpu_material->inputs : NULL);
|
|
GPUInput *input;
|
|
|
|
if (shader == NULL) {
|
|
return;
|
|
}
|
|
|
|
GPU_shader_bind(shader);
|
|
|
|
for (input = inputs->first;
|
|
input != NULL;
|
|
input = input->next)
|
|
{
|
|
if (input->source == GPU_SOURCE_ATTRIB &&
|
|
input->attribtype == CD_MTFACE)
|
|
{
|
|
char name[64];
|
|
/* TODO(sergey): This will work for until names are
|
|
* consistent, we'll need to solve this somehow in the future.
|
|
*/
|
|
int layer_index;
|
|
int location;
|
|
|
|
if (input->attribname[0] != '\0') {
|
|
layer_index = CustomData_get_named_layer(&dm->loopData,
|
|
CD_MLOOPUV,
|
|
input->attribname);
|
|
}
|
|
else {
|
|
layer_index = CustomData_get_active_layer(&dm->loopData,
|
|
CD_MLOOPUV);
|
|
}
|
|
|
|
BLI_snprintf(name, sizeof(name),
|
|
"fvar%d_offset",
|
|
input->attribid);
|
|
location = GPU_shader_get_uniform(shader, name);
|
|
GPU_shader_uniform_int(shader, location, layer_index);
|
|
}
|
|
}
|
|
|
|
GPU_shader_unbind();
|
|
}
|
|
#endif
|