GPUMaterial: Optimize and fix blending in box mapping

Blending was done in srgb space and was not matching cycles.

Optimized by using less branches and more vector operations.
This commit is contained in:
Clément Foucault 2018-07-20 15:25:20 +02:00
parent 9ad5eafe46
commit e7d8908f14
2 changed files with 78 additions and 69 deletions

View File

@ -1804,18 +1804,46 @@ void node_tex_image(vec3 co, sampler2D ima, out vec4 color, out float alpha)
alpha = color.a;
}
void tex_box_sample(vec3 texco,
vec3 N,
sampler2D ima,
out vec4 color1,
out vec4 color2,
out vec4 color3)
{
/* X projection */
vec2 uv = texco.yz;
if (N.x < 0.0) {
uv.x = 1.0 - uv.x;
}
color1 = texture(ima, uv);
/* Y projection */
uv = texco.xz;
if (N.y > 0.0) {
uv.x = 1.0 - uv.x;
}
color2 = texture(ima, uv);
/* Z projection */
uv = texco.yx;
if (N.z > 0.0) {
uv.x = 1.0 - uv.x;
}
color3 = texture(ima, uv);
}
void node_tex_image_box(vec3 texco,
vec3 N,
vec4 color1,
vec4 color2,
vec4 color3,
sampler2D ima,
float blend,
out vec4 color,
out float alpha)
{
vec3 signed_N = N;
/* project from direction vector to barycentric coordinates in triangles */
N = vec3(abs(N.x), abs(N.y), abs(N.z));
N /= (N.x + N.y + N.z);
N = abs(N);
N /= dot(N, vec3(1.0));
/* basic idea is to think of this as a triangle, each corner representing
* one of the 3 faces of the cube. in the corners we have single textures,
@ -1825,72 +1853,36 @@ void node_tex_image_box(vec3 texco,
* the Nxyz values are the barycentric coordinates in an equilateral
* triangle, which in case of blending, in the middle has a smaller
* equilateral triangle where 3 textures blend. this divides things into
* 7 zones, with an if () test for each zone */
* 7 zones, with an if () test for each zone
* EDIT: Now there is only 4 if's. */
vec3 weight = vec3(0.0, 0.0, 0.0);
float limit = 0.5 * (1.0 + blend);
float limit = 0.5 + 0.5 * blend;
/* first test for corners with single texture */
if (N.x > limit * (N.x + N.y) && N.x > limit * (N.x + N.z)) {
weight.x = 1.0;
vec3 weight;
weight.x = N.x / (N.x + N.y);
weight.y = N.y / (N.y + N.z);
weight.z = N.z / (N.x + N.z);
weight = clamp((weight - 0.5 * (1.0 - blend)) / max(1e-8, blend), 0.0, 1.0);
/* test for mixes between two textures */
if (N.z < (1.0 - limit) * (N.y + N.x)) {
weight.z = 0.0;
weight.y = 1.0 - weight.x;
}
else if (N.y > limit * (N.x + N.y) && N.y > limit * (N.y + N.z)) {
weight.y = 1.0;
else if (N.x < (1.0 - limit) * (N.y + N.z)) {
weight.x = 0.0;
weight.z = 1.0 - weight.y;
}
else if (N.z > limit * (N.x + N.z) && N.z > limit * (N.y + N.z)) {
weight.z = 1.0;
}
else if (blend > 0.0) {
/* in case of blending, test for mixes between two textures */
if (N.z < (1.0 - limit) * (N.y + N.x)) {
weight.x = N.x / (N.x + N.y);
weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
weight.y = 1.0 - weight.x;
}
else if (N.x < (1.0 - limit) * (N.y + N.z)) {
weight.y = N.y / (N.y + N.z);
weight.y = clamp((weight.y - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
weight.z = 1.0 - weight.y;
}
else if (N.y < (1.0 - limit) * (N.x + N.z)) {
weight.x = N.x / (N.x + N.z);
weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
weight.z = 1.0 - weight.x;
}
else {
/* last case, we have a mix between three */
weight.x = ((2.0 - limit) * N.x + (limit - 1.0)) / (2.0 * limit - 1.0);
weight.y = ((2.0 - limit) * N.y + (limit - 1.0)) / (2.0 * limit - 1.0);
weight.z = ((2.0 - limit) * N.z + (limit - 1.0)) / (2.0 * limit - 1.0);
}
else if (N.y < (1.0 - limit) * (N.x + N.z)) {
weight.y = 0.0;
weight.x = 1.0 - weight.z;
}
else {
/* Desperate mode, no valid choice anyway, fallback to one side.*/
weight.x = 1.0;
}
color = vec4(0);
if (weight.x > 0.0) {
vec2 uv = texco.yz;
if(signed_N.x < 0.0) {
uv.x = 1.0 - uv.x;
}
color += weight.x * texture(ima, uv);
}
if (weight.y > 0.0) {
vec2 uv = texco.xz;
if(signed_N.y > 0.0) {
uv.x = 1.0 - uv.x;
}
color += weight.y * texture(ima, uv);
}
if (weight.z > 0.0) {
vec2 uv = texco.yx;
if(signed_N.z > 0.0) {
uv.x = 1.0 - uv.x;
}
color += weight.z * texture(ima, uv);
/* last case, we have a mix between three */
weight = ((2.0 - limit) * N + (limit - 1.0)) / max(1e-8, 2.0 * limit - 1.0);
}
color = weight.x * color1 + weight.y * color2 + weight.z * color3;
alpha = color.a;
}

View File

@ -58,8 +58,9 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat
Image *ima = (Image *)node->id;
ImageUser *iuser = NULL;
NodeTexImage *tex = node->storage;
bool do_color_correction = false;
GPUNodeLink *norm;
GPUNodeLink *norm, *col1, *col2, *col3;
int isdata = tex->color_space == SHD_COLORSPACE_NONE;
float blend = tex->projection_blend;
@ -67,6 +68,15 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat
if (!ima)
return GPU_stack_link(mat, node, "node_tex_image_empty", in, out);
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
if ((tex->color_space == SHD_COLORSPACE_COLOR) &&
ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 &&
GPU_material_do_color_management(mat))
{
do_color_correction = true;
}
BKE_image_release_ibuf(ima, ibuf, NULL);
if (!in[0].link)
in[0].link = GPU_attribute(CD_MTFACE, "");
@ -83,8 +93,20 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat
GPU_link(mat, "direction_transform_m4v3", norm,
GPU_builtin(GPU_INVERSE_OBJECT_MATRIX),
&norm);
GPU_link(mat, "tex_box_sample", in[0].link,
norm,
GPU_image(ima, iuser, isdata),
&col1,
&col2,
&col3);
if (do_color_correction) {
GPU_link(mat, "srgb_to_linearrgb", col1, &col1);
GPU_link(mat, "srgb_to_linearrgb", col2, &col2);
GPU_link(mat, "srgb_to_linearrgb", col3, &col3);
}
GPU_link(mat, "node_tex_image_box", in[0].link,
norm,
col1, col2, col3,
GPU_image(ima, iuser, isdata),
GPU_uniform(&blend),
&out[0].link,
@ -102,14 +124,9 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat
break;
}
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
if ((tex->color_space == SHD_COLORSPACE_COLOR) &&
ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 &&
GPU_material_do_color_management(mat))
{
if (do_color_correction && (tex->projection != SHD_PROJ_BOX)) {
GPU_link(mat, "srgb_to_linearrgb", out[0].link, &out[0].link);
}
BKE_image_release_ibuf(ima, ibuf, NULL);
return true;
}