/** * @file class1\deferred\pbrterrainUtilF.glsl * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2023, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ /** * Triplanar mapping implementation adapted from Inigo Quilez' example shader, * MIT license. * https://www.shadertoy.com/view/MtsGWH * Copyright © 2015 Inigo Quilez * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: The above copyright * notice and this permission notice shall be included in all copies or * substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", * WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ in vec3 vary_vertex_normal; vec3 srgb_to_linear(vec3 c); float terrain_mix(vec4 samples, float alpha1, float alpha2, float alphaFinal) { return mix( mix(samples.w, samples.z, alpha2), mix(samples.y, samples.x, alpha1), alphaFinal ); } vec3 terrain_mix(vec3[4] samples, float alpha1, float alpha2, float alphaFinal) { return mix( mix(samples[3], samples[2], alpha2), mix(samples[1], samples[0], alpha1), alphaFinal ); } vec4 terrain_mix(vec4[4] samples, float alpha1, float alpha2, float alphaFinal) { return mix( mix(samples[3], samples[2], alpha2), mix(samples[1], samples[0], alpha1), alphaFinal ); } #if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 // Triplanar mapping // Pre-transformed texture coordinates for each axial uv slice (Packing: xy, yz, (-x)z, unused) #define TerrainCoord vec4[2] vec4 _t_texture(sampler2D tex, vec2 uv_unflipped, float sign) { // If the vertex normal is negative, flip the texture back // right-side up. vec2 uv = uv_unflipped * vec2(sign, 1); return texture(tex, uv); } vec3 _t_texture_n(sampler2D tex, vec2 uv_unflipped, float sign) { // Unpack normal from pixel to vector vec3 n = _t_texture(tex, uv_unflipped, sign).xyz*2.0-1.0; // If the sign is negative, rotate normal by 180 degrees n.xy = (min(0, sign) * n.xy) + (min(0, -sign) * -n.xy); return n; } vec4 terrain_texture(sampler2D tex, TerrainCoord terrain_coord) { // Multiplying the UVs by the sign of the normal flips the texture upright. vec4 x = _t_texture(tex, terrain_coord[0].zw, sign(vary_vertex_normal.x)); vec4 y = _t_texture(tex, terrain_coord[1].xy, sign(vary_vertex_normal.y)); vec4 z = _t_texture(tex, terrain_coord[0].xy, sign(vary_vertex_normal.z)); float sharpness = TERRAIN_TRIPLANAR_BLEND_FACTOR; vec3 weight = pow(abs(vary_vertex_normal), vec3(sharpness)); return ((x * weight.x) + (y * weight.y) + (z * weight.z)) / (weight.x + weight.y + weight.z); } // Specialized triplanar normal texture sampling implementation, taking into // account how the rotation of the texture affects the lighting and trying to // negate that. // *TODO: Decide if we want this. It may be better to just calculate the // tangents on-the-fly here rather than messing with the normals, due to the // subtleties of the effects of triplanar mapping on UVs. These sampled normals // are only valid on the faces of a cube, and the pregenerated tangents are // only valid for uv = xy. // *NOTE: Bottom face has not been tested vec3 terrain_texture_normal(sampler2D tex, TerrainCoord terrain_coord) { float sharpness = TERRAIN_TRIPLANAR_BLEND_FACTOR; vec3 weight = pow(abs(vary_vertex_normal), vec3(sharpness)); float threshold = 0.01; vec3 significant = max(vec3(0), sign(weight - threshold)); int sample_type = (int(significant.x) << 2) | (int(significant.y) << 1) | (int(significant.z) << 0); #define SAMPLE_X 1 << 2 #define SAMPLE_Y 1 << 1 #define SAMPLE_Z 1 << 0 #define terrain_coord_x terrain_coord[0].zw #define terrain_coord_y terrain_coord[1].xy #define terrain_coord_z terrain_coord[0].xy vec3 x; vec3 y; vec3 z; switch (sample_type) { case SAMPLE_X | SAMPLE_Y | SAMPLE_Z: x = _t_texture_n(tex, terrain_coord_x, sign(vary_vertex_normal.x)); y = _t_texture_n(tex, terrain_coord_y, sign(vary_vertex_normal.y)); z = _t_texture_n(tex, terrain_coord_z, sign(vary_vertex_normal.z)); break; case SAMPLE_X | SAMPLE_Y: x = _t_texture_n(tex, terrain_coord_x, sign(vary_vertex_normal.x)); y = _t_texture_n(tex, terrain_coord_y, sign(vary_vertex_normal.y)); z = vec3(0); break; case SAMPLE_X | SAMPLE_Z: x = _t_texture_n(tex, terrain_coord_x, sign(vary_vertex_normal.x)); y = vec3(0); z = _t_texture_n(tex, terrain_coord_z, sign(vary_vertex_normal.z)); break; case SAMPLE_Y | SAMPLE_Z: x = vec3(0); y = _t_texture_n(tex, terrain_coord_y, sign(vary_vertex_normal.y)); z = _t_texture_n(tex, terrain_coord_z, sign(vary_vertex_normal.z)); break; case SAMPLE_X: x = _t_texture_n(tex, terrain_coord_x, sign(vary_vertex_normal.x)); y = vec3(0); z = vec3(0); break; case SAMPLE_Y: x = vec3(0); y = _t_texture_n(tex, terrain_coord_y, sign(vary_vertex_normal.y)); z = vec3(0); break; case SAMPLE_Z: default: x = vec3(0); y = vec3(0); z = _t_texture_n(tex, terrain_coord_z, sign(vary_vertex_normal.z)); break; } // *HACK: Transform normals according to orientation of the UVs x.xy = vec2(-x.y, x.x); y.xy = -y.xy; return ((x * weight.x) + (y * weight.y) + (z * weight.z)) / (weight.x + weight.y + weight.z); } #elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1 #define TerrainCoord vec2 vec4 terrain_texture(sampler2D tex, TerrainCoord terrain_coord) { return texture(tex, terrain_coord); } vec3 terrain_texture_normal(sampler2D tex, TerrainCoord terrain_coord) { return texture(tex, terrain_coord).xyz*2.0-1.0; } #endif vec3 sample_and_mix_color3(float alpha1, float alpha2, float alphaFinal, TerrainCoord texcoord, vec3[4] factors, sampler2D tex0, sampler2D tex1, sampler2D tex2, sampler2D tex3) { vec3[4] samples; samples[0] = terrain_texture(tex0, texcoord).xyz; samples[1] = terrain_texture(tex1, texcoord).xyz; samples[2] = terrain_texture(tex2, texcoord).xyz; samples[3] = terrain_texture(tex3, texcoord).xyz; samples[0] = srgb_to_linear(samples[0]); samples[1] = srgb_to_linear(samples[1]); samples[2] = srgb_to_linear(samples[2]); samples[3] = srgb_to_linear(samples[3]); samples[0] *= factors[0]; samples[1] *= factors[1]; samples[2] *= factors[2]; samples[3] *= factors[3]; return terrain_mix(samples, alpha1, alpha2, alphaFinal); } vec4 sample_and_mix_color4(float alpha1, float alpha2, float alphaFinal, TerrainCoord texcoord, vec4[4] factors, sampler2D tex0, sampler2D tex1, sampler2D tex2, sampler2D tex3) { vec4[4] samples; samples[0] = terrain_texture(tex0, texcoord); samples[1] = terrain_texture(tex1, texcoord); samples[2] = terrain_texture(tex2, texcoord); samples[3] = terrain_texture(tex3, texcoord); samples[0].xyz = srgb_to_linear(samples[0].xyz); samples[1].xyz = srgb_to_linear(samples[1].xyz); samples[2].xyz = srgb_to_linear(samples[2].xyz); samples[3].xyz = srgb_to_linear(samples[3].xyz); samples[0] *= factors[0]; samples[1] *= factors[1]; samples[2] *= factors[2]; samples[3] *= factors[3]; return terrain_mix(samples, alpha1, alpha2, alphaFinal); } vec3 sample_and_mix_vector3(float alpha1, float alpha2, float alphaFinal, TerrainCoord texcoord, vec3[4] factors, sampler2D tex0, sampler2D tex1, sampler2D tex2, sampler2D tex3) { vec3[4] samples; samples[0] = terrain_texture(tex0, texcoord).xyz; samples[1] = terrain_texture(tex1, texcoord).xyz; samples[2] = terrain_texture(tex2, texcoord).xyz; samples[3] = terrain_texture(tex3, texcoord).xyz; samples[0] *= factors[0]; samples[1] *= factors[1]; samples[2] *= factors[2]; samples[3] *= factors[3]; return terrain_mix(samples, alpha1, alpha2, alphaFinal); } // Returns the unpacked normal texture in range [-1, 1] vec3 sample_and_mix_normal(float alpha1, float alpha2, float alphaFinal, TerrainCoord texcoord, sampler2D tex0, sampler2D tex1, sampler2D tex2, sampler2D tex3) { vec3[4] samples; samples[0] = terrain_texture_normal(tex0, texcoord).xyz; samples[1] = terrain_texture_normal(tex1, texcoord).xyz; samples[2] = terrain_texture_normal(tex2, texcoord).xyz; samples[3] = terrain_texture_normal(tex3, texcoord).xyz; return terrain_mix(samples, alpha1, alpha2, alphaFinal); }