mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
ReignEngine: Implement basic lighting
This implements a Phong shading with ambient, diffuse and specular components, using up to three directional lights and one specular light. The light calculation is made to match the "high quality" 3D mode of Toushin Toshi 3.
This commit is contained in:
+9
-4
@@ -91,9 +91,7 @@ struct RE_plugin {
|
||||
bool use_power2_texture;
|
||||
int shadow_map_resolution_level;
|
||||
int texture_resolution_level;
|
||||
float global_ambient_r;
|
||||
float global_ambient_g;
|
||||
float global_ambient_b;
|
||||
vec3 global_ambient;
|
||||
float post_effect_filter_y;
|
||||
float post_effect_filter_cb;
|
||||
float post_effect_filter_cr;
|
||||
@@ -108,16 +106,23 @@ struct RE_instance {
|
||||
enum RE_instance_type type;
|
||||
vec3 pos;
|
||||
float pitch, roll, yaw; // in degrees
|
||||
vec3 vec;
|
||||
vec3 scale;
|
||||
float alpha;
|
||||
bool draw;
|
||||
float fps;
|
||||
bool motion_blend;
|
||||
float motion_blend_rate;
|
||||
vec3 ambient;
|
||||
|
||||
// Lights
|
||||
vec3 vec;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
|
||||
// Private
|
||||
bool local_transform_needs_update;
|
||||
mat4 local_transform;
|
||||
mat3 normal_transform;
|
||||
mat4 *bone_transforms; // model->nr_bones elements
|
||||
};
|
||||
|
||||
|
||||
+37
-2
@@ -14,14 +14,49 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
};
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float alpha_mod;
|
||||
|
||||
uniform vec3 ambient;
|
||||
uniform dir_light dir_lights[NR_DIR_LIGHTS];
|
||||
uniform vec3 specular_light_dir;
|
||||
uniform float specular_strength;
|
||||
uniform float specular_shininess;
|
||||
uniform vec3 view_pos;
|
||||
|
||||
in vec2 tex_coord;
|
||||
in vec3 normal;
|
||||
in vec3 frag_pos;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
// TODO: Implement lighting.
|
||||
vec3 norm = normalize(normal);
|
||||
vec3 frag_rgb = ambient;
|
||||
|
||||
// Diffuse lighting
|
||||
vec3 diffuse = vec3(0.0);
|
||||
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
|
||||
float half_lambert = dot(norm, -dir_lights[i].dir) * 0.5 + 0.5;
|
||||
diffuse += mix(dir_lights[i].globe_diffuse, dir_lights[i].diffuse, half_lambert);
|
||||
}
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
frag_color = vec4(texel.rgb, texel.a * alpha_mod);
|
||||
frag_rgb += texel.rgb * diffuse;
|
||||
|
||||
// Specular lighting
|
||||
if (specular_strength > 0.0) {
|
||||
vec3 view_dir = normalize(view_pos - frag_pos);
|
||||
vec3 reflect_dir = reflect(specular_light_dir, norm);
|
||||
float specular = pow(max(dot(view_dir, reflect_dir), 0.0), specular_shininess) * specular_strength;
|
||||
frag_rgb += vec3(specular);
|
||||
}
|
||||
|
||||
frag_color = vec4(frag_rgb, texel.a * alpha_mod);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
uniform mat3 normal_transform;
|
||||
|
||||
const int MAX_BONES = 211; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
@@ -30,6 +31,8 @@ in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
|
||||
out vec2 tex_coord;
|
||||
out vec3 normal;
|
||||
out vec3 frag_pos;
|
||||
|
||||
void main() {
|
||||
mat4 bone_transform = mat4(1.0f);
|
||||
@@ -42,6 +45,10 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
gl_Position = proj_transform * view_transform * local_transform * bone_transform * vec4(vertex_pos, 1.0);
|
||||
vec4 world_pos = local_transform * bone_transform * vec4(vertex_pos, 1.0);
|
||||
gl_Position = proj_transform * view_transform * world_pos;
|
||||
normal = normal_transform * mat3(bone_transform) * vertex_normal;
|
||||
|
||||
tex_coord = vertex_uv;
|
||||
frag_pos = vec3(world_pos);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "gfx/gl.h"
|
||||
#include "gfx/gfx.h"
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define MAX_BONES 211 // Maximum in TT3
|
||||
|
||||
struct model {
|
||||
@@ -75,9 +76,21 @@ struct RE_renderer {
|
||||
// Uniform variable locations
|
||||
GLint local_transform;
|
||||
GLint proj_transform;
|
||||
GLint normal_transform;
|
||||
GLint alpha_mod;
|
||||
GLint has_bones;
|
||||
GLint bone_matrices;
|
||||
GLint ambient;
|
||||
struct {
|
||||
GLint dir;
|
||||
GLint diffuse;
|
||||
GLint globe_diffuse;
|
||||
GLint ambient;
|
||||
} dir_lights[NR_DIR_LIGHTS];
|
||||
GLint specular_light_dir;
|
||||
GLint specular_strength;
|
||||
GLint specular_shininess;
|
||||
GLint view_pos;
|
||||
|
||||
// Attribute variable locations
|
||||
GLint vertex_normal;
|
||||
|
||||
+16
-8
@@ -21,6 +21,8 @@
|
||||
#include "sprite.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define SPREAD_VEC3(v) (v)[0], (v)[1], (v)[2]
|
||||
|
||||
static const char *instance_type_name(enum RE_instance_type type)
|
||||
{
|
||||
switch (type) {
|
||||
@@ -65,17 +67,24 @@ static void print_instance(struct RE_instance *inst, int index, int indent)
|
||||
indent++;
|
||||
|
||||
indent_printf(indent, "type = %s,\n", instance_type_name(inst->type));
|
||||
indent_printf(indent, "draw = %d,\n", inst->draw);
|
||||
indent_printf(indent, "pos = {x=%f, y=%f, z=%f},\n", inst->pos[0], inst->pos[1], inst->pos[2]);
|
||||
indent_printf(indent, "vec = {x=%f, y=%f, z=%f},\n", inst->vec[0], inst->vec[1], inst->vec[2]);
|
||||
indent_printf(indent, "scale = {x=%f, y=%f, z=%f},\n", inst->scale[0], inst->scale[1], inst->scale[2]);
|
||||
if (inst->type == RE_ITYPE_DIRECTIONAL_LIGHT || inst->type == RE_ITYPE_SPECULAR_LIGHT) {
|
||||
indent_printf(indent, "vec = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->vec));
|
||||
indent_printf(indent, "diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->diffuse));
|
||||
indent_printf(indent, "globe_diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->globe_diffuse));
|
||||
} else {
|
||||
indent_printf(indent, "draw = %d,\n", inst->draw);
|
||||
indent_printf(indent, "pos = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->pos));
|
||||
indent_printf(indent, "scale = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->scale));
|
||||
indent_printf(indent, "ambient = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->ambient));
|
||||
}
|
||||
if (inst->model)
|
||||
indent_printf(indent, "path = \"%s\",\n", inst->model->path);
|
||||
if (inst->motion)
|
||||
if (inst->motion) {
|
||||
indent_printf(indent, "fps = %f,\n", inst->fps);
|
||||
print_motion("motion", inst->motion, indent);
|
||||
}
|
||||
if (inst->next_motion)
|
||||
print_motion("next_motion", inst->next_motion, indent);
|
||||
indent_printf(indent, "fps = %f,\n", inst->fps);
|
||||
if (inst->motion_blend)
|
||||
indent_printf(indent, "motion_blend_rate = %f,\n", inst->motion_blend_rate);
|
||||
|
||||
@@ -109,8 +118,7 @@ void RE_debug_print(struct sact_sprite *sp, int indent)
|
||||
|
||||
indent_printf(indent, "name = \"%s\",\n", p->plugin.name);
|
||||
indent_printf(indent, "camera = {x=%f, y=%f, z=%f, pitch=%f, roll=%f, yaw=%f},\n",
|
||||
p->camera.pos[0], p->camera.pos[1], p->camera.pos[2],
|
||||
p->camera.pitch, p->camera.roll, p->camera.yaw);
|
||||
SPREAD_VEC3(p->camera.pos), p->camera.pitch, p->camera.roll, p->camera.yaw);
|
||||
|
||||
for (int i = 0; i < RE_MAX_INSTANCES; i++)
|
||||
print_instance(p->instances[i], i, indent);
|
||||
|
||||
@@ -42,6 +42,7 @@ static struct RE_instance *create_instance(struct RE_plugin *plugin)
|
||||
instance->alpha = 1.0;
|
||||
instance->fps = 30.0;
|
||||
glm_mat4_identity(instance->local_transform);
|
||||
glm_mat3_identity(instance->normal_transform);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
+82
-1
@@ -65,9 +65,24 @@ struct RE_renderer *RE_renderer_new(struct texture *texture)
|
||||
gfx_load_shader(&r->shader, "shaders/reign.v.glsl", "shaders/reign.f.glsl");
|
||||
r->local_transform = glGetUniformLocation(r->shader.program, "local_transform");
|
||||
r->proj_transform = glGetUniformLocation(r->shader.program, "proj_transform");
|
||||
r->normal_transform = glGetUniformLocation(r->shader.program, "normal_transform");
|
||||
r->alpha_mod = glGetUniformLocation(r->shader.program, "alpha_mod");
|
||||
r->has_bones = glGetUniformLocation(r->shader.program, "has_bones");
|
||||
r->bone_matrices = glGetUniformLocation(r->shader.program, "bone_matrices");
|
||||
r->ambient = glGetUniformLocation(r->shader.program, "ambient");
|
||||
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
|
||||
char buf[64];
|
||||
sprintf(buf, "dir_lights[%d].dir", i);
|
||||
r->dir_lights[i].dir = glGetUniformLocation(r->shader.program, buf);
|
||||
sprintf(buf, "dir_lights[%d].diffuse", i);
|
||||
r->dir_lights[i].diffuse = glGetUniformLocation(r->shader.program, buf);
|
||||
sprintf(buf, "dir_lights[%d].globe_diffuse", i);
|
||||
r->dir_lights[i].globe_diffuse = glGetUniformLocation(r->shader.program, buf);
|
||||
}
|
||||
r->specular_light_dir = glGetUniformLocation(r->shader.program, "specular_light_dir");
|
||||
r->specular_strength = glGetUniformLocation(r->shader.program, "specular_strength");
|
||||
r->specular_shininess = glGetUniformLocation(r->shader.program, "specular_shininess");
|
||||
r->view_pos = glGetUniformLocation(r->shader.program, "view_pos");
|
||||
r->vertex_normal = glGetAttribLocation(r->shader.program, "vertex_normal");
|
||||
r->vertex_uv = glGetAttribLocation(r->shader.program, "vertex_uv");
|
||||
r->vertex_bone_index = glGetAttribLocation(r->shader.program, "vertex_bone_index");
|
||||
@@ -156,6 +171,11 @@ static void update_local_transform(struct RE_instance *inst)
|
||||
glm_mat4_mul(inst->local_transform, rot, inst->local_transform);
|
||||
glm_scale(inst->local_transform, inst->scale);
|
||||
|
||||
mat3 normal;
|
||||
glm_mat4_pick3(inst->local_transform, normal);
|
||||
glm_mat3_inv(normal, normal);
|
||||
glm_mat3_transpose_to(normal, inst->normal_transform);
|
||||
|
||||
inst->local_transform_needs_update = false;
|
||||
}
|
||||
|
||||
@@ -236,12 +256,26 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r)
|
||||
update_local_transform(inst);
|
||||
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, inst->local_transform[0]);
|
||||
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, inst->normal_transform[0]);
|
||||
glUniform1f(r->alpha_mod, inst->alpha);
|
||||
vec3 ambient;
|
||||
glm_vec3_add(inst->plugin->global_ambient, inst->ambient, ambient);
|
||||
glUniform3fv(r->ambient, 1, ambient);
|
||||
|
||||
for (int i = 0; i < model->nr_meshes; i++) {
|
||||
struct mesh *mesh = &model->meshes[i];
|
||||
struct material *material = &model->materials[mesh->material];
|
||||
|
||||
if (inst->plugin->specular_mode) {
|
||||
glUniform1f(r->specular_strength, material->specular_strength);
|
||||
glUniform1f(r->specular_shininess, material->specular_shininess);
|
||||
} else {
|
||||
glUniform1f(r->specular_strength, 0.0);
|
||||
glUniform1f(r->specular_shininess, 0.0);
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, model->materials[mesh->material].color_map);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_map);
|
||||
glUniform1i(r->shader.texture, 0);
|
||||
|
||||
glBindVertexArray(mesh->vao);
|
||||
@@ -288,10 +322,20 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma
|
||||
glm_mat4_pick3t(view_mat, rot);
|
||||
glm_mat4_ins3(rot, local_transform);
|
||||
glm_scale(local_transform, inst->scale);
|
||||
mat3 normal_transform;
|
||||
// This should be safe because billboards do not have non-uniform scaling.
|
||||
glm_mat4_pick3(local_transform, normal_transform);
|
||||
|
||||
vec3 ambient;
|
||||
glm_vec3_add(inst->plugin->global_ambient, inst->ambient, ambient);
|
||||
glUniform3fv(r->ambient, 1, ambient);
|
||||
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, local_transform[0]);
|
||||
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, normal_transform[0]);
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
glUniform1f(r->alpha_mod, inst->alpha);
|
||||
glUniform1f(r->specular_strength, 0.0);
|
||||
glUniform1f(r->specular_shininess, 0.0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, bt->texture);
|
||||
@@ -313,6 +357,41 @@ static void render_back_cg(struct texture *dst, struct RE_back_cg *bcg, struct R
|
||||
gfx_copy_stretch_blend(dst, bcg->x, bcg->y, sw * bcg->mag, sh * bcg->mag, &bcg->texture, 0, 0, sw, sh, bcg->blend_rate * 255);
|
||||
}
|
||||
|
||||
static void setup_lights(struct RE_plugin *plugin)
|
||||
{
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
glUniform3fv(r->view_pos, 1, plugin->camera.pos);
|
||||
int light_index = 0;
|
||||
for (int i = 0; i < RE_MAX_INSTANCES; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
if (!inst)
|
||||
continue;
|
||||
switch (inst->type) {
|
||||
case RE_ITYPE_DIRECTIONAL_LIGHT:
|
||||
if (light_index >= NR_DIR_LIGHTS) {
|
||||
WARNING("too many directional lights");
|
||||
break;
|
||||
}
|
||||
glUniform3fv(r->dir_lights[light_index].dir, 1, inst->vec);
|
||||
glUniform3fv(r->dir_lights[light_index].diffuse, 1, inst->diffuse);
|
||||
glUniform3fv(r->dir_lights[light_index].globe_diffuse, 1, inst->globe_diffuse);
|
||||
light_index++;
|
||||
break;
|
||||
case RE_ITYPE_SPECULAR_LIGHT:
|
||||
glUniform3fv(r->specular_light_dir, 1, inst->vec);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; light_index < NR_DIR_LIGHTS; light_index++) {
|
||||
const vec3 zero = {0.0, 0.0, 0.0};
|
||||
glUniform3fv(r->dir_lights[light_index].dir, 1, zero);
|
||||
glUniform3fv(r->dir_lights[light_index].diffuse, 1, zero);
|
||||
glUniform3fv(r->dir_lights[light_index].globe_diffuse, 1, zero);
|
||||
}
|
||||
}
|
||||
|
||||
void RE_render(struct sact_sprite *sp)
|
||||
{
|
||||
uint32_t timestamp = SDL_GetTicks();
|
||||
@@ -363,6 +442,8 @@ void RE_render(struct sact_sprite *sp)
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
setup_lights(plugin);
|
||||
|
||||
for (int i = 0; i < RE_MAX_INSTANCES; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
if (!inst || !inst->draw)
|
||||
|
||||
+43
-11
@@ -14,6 +14,7 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cglm/cglm.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "system4.h"
|
||||
@@ -135,6 +136,7 @@ static bool ReignEngine_SetInstanceVector(int plugin, int instance, float x, flo
|
||||
ri->vec[0] = x;
|
||||
ri->vec[1] = y;
|
||||
ri->vec[2] = -z;
|
||||
glm_vec3_normalize(ri->vec);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -219,14 +221,44 @@ static bool ReignEngine_SetInstanceScaleZ(int plugin, int instance, float scale_
|
||||
//bool ReignEngine_SetInstanceZBias(int plugin, int instance, float fZBias);
|
||||
//float ReignEngine_GetInstanceZBias(int plugin, int instance);
|
||||
|
||||
bool ReignEngine_SetInstanceVertexPos(int plugin, int instance, int index, float x, float y, float z)
|
||||
static bool ReignEngine_SetInstanceVertexPos(int plugin, int instance, int index, float x, float y, float z)
|
||||
{
|
||||
return RE_instance_set_vertex_pos(get_instance(plugin, instance), index, x, y, z);
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceDiffuse, int plugin, int instance, float r, float g, float b);
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceGlobeDiffuse, int plugin, int instance, float r, float g, float b);
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceAmbient, int plugin, int instance, float r, float g, float b);
|
||||
static bool ReignEngine_SetInstanceDiffuse(int plugin, int instance, float r, float g, float b)
|
||||
{
|
||||
struct RE_instance *ri = get_instance(plugin, instance);
|
||||
if (!ri)
|
||||
return false;
|
||||
ri->diffuse[0] = r;
|
||||
ri->diffuse[1] = g;
|
||||
ri->diffuse[2] = b;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ReignEngine_SetInstanceGlobeDiffuse(int plugin, int instance, float r, float g, float b)
|
||||
{
|
||||
struct RE_instance *ri = get_instance(plugin, instance);
|
||||
if (!ri)
|
||||
return false;
|
||||
ri->globe_diffuse[0] = r;
|
||||
ri->globe_diffuse[1] = g;
|
||||
ri->globe_diffuse[2] = b;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ReignEngine_SetInstanceAmbient(int plugin, int instance, float r, float g, float b)
|
||||
{
|
||||
struct RE_instance *ri = get_instance(plugin, instance);
|
||||
if (!ri)
|
||||
return false;
|
||||
ri->ambient[0] = r;
|
||||
ri->ambient[1] = g;
|
||||
ri->ambient[2] = b;
|
||||
return true;
|
||||
}
|
||||
|
||||
//bool ReignEngine_SetInstanceSpecular(int plugin, int instance, float fSpecular);
|
||||
|
||||
static bool ReignEngine_SetInstanceAlpha(int plugin, int instance, float alpha)
|
||||
@@ -882,9 +914,9 @@ static bool ReignEngine_GetGlobalAmbient(int plugin, float *r, float *g, float *
|
||||
struct RE_plugin *p = get_plugin(plugin);
|
||||
if (!p)
|
||||
return false;
|
||||
*r = p->global_ambient_r;
|
||||
*g = p->global_ambient_g;
|
||||
*b = p->global_ambient_b;
|
||||
*r = p->global_ambient[0];
|
||||
*g = p->global_ambient[1];
|
||||
*b = p->global_ambient[2];
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -893,9 +925,9 @@ static bool ReignEngine_SetGlobalAmbient(int plugin, float r, float g, float b)
|
||||
struct RE_plugin *p = get_plugin(plugin);
|
||||
if (!p)
|
||||
return false;
|
||||
p->global_ambient_r = r;
|
||||
p->global_ambient_g = g;
|
||||
p->global_ambient_b = b;
|
||||
p->global_ambient[0] = r;
|
||||
p->global_ambient[1] = g;
|
||||
p->global_ambient[2] = b;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1085,7 +1117,7 @@ HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, IsLoading, int plugin);
|
||||
//bool ReignEngine_SetDebugInfoMode(int plugin, int nMode);
|
||||
HLL_WARN_UNIMPLEMENTED(30, int, ReignEngine, GetVertexShaderVersion, void);
|
||||
HLL_WARN_UNIMPLEMENTED(30, int, ReignEngine, GetPixelShaderVersion, void);
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceSpecularReflectRate, int plugin, int instance, float rate);
|
||||
HLL_QUIET_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceSpecularReflectRate, int plugin, int instance, float rate); // the value is unused?
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceFresnelReflectRate, int plugin, int instance, float rate);
|
||||
//float ReignEngine_GetInstanceSpecularReflectRate(int plugin, int instance);
|
||||
//float ReignEngine_GetInstanceFresnelReflectRate(int plugin, int instance);
|
||||
|
||||
Reference in New Issue
Block a user