SL-17286 Reflection probe alpha/fullbright support.

meow-7.2.2
Dave Parks 2022-05-19 22:24:41 -05:00
parent 02fb1bd610
commit 3564b24e2a
13 changed files with 685 additions and 490 deletions

View File

@ -58,6 +58,7 @@ public:
S32 mIndexedTextureChannels;
bool disableTextureIndex;
bool hasAlphaMask;
bool hasReflectionProbes = false;
bool attachNothing;
// char numLights;

View File

@ -218,6 +218,14 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader)
}
}
if (features->hasReflectionProbes)
{
if (!shader->attachFragmentObject("deferred/reflectionProbeF.glsl"))
{
return FALSE;
}
}
if (features->hasAmbientOcclusion)
{
if (!shader->attachFragmentObject("deferred/aoUtil.glsl"))

View File

@ -35,10 +35,11 @@ out vec4 frag_color;
uniform sampler2D diffuseMap;
#endif
VARYING vec4 vertex_color;
VARYING vec2 vary_texcoord0;
VARYING vec3 vary_texcoord1;
VARYING vec4 vary_position;
VARYING vec3 vary_position;
uniform samplerCube environmentMap;
@ -54,6 +55,14 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou
vec3 linear_to_srgb(vec3 c);
vec3 srgb_to_linear(vec3 c);
#ifdef HAS_REFLECTION_PROBES
// reflection probe interface
void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyEnv,
vec3 pos, vec3 norm, float glossiness, float envIntensity);
void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm);
void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity);
#endif
// See:
// class1\deferred\fullbrightShinyF.glsl
// class1\lighting\lightFullbrightShinyF.glsl
@ -70,21 +79,29 @@ void main()
// SL-9632 HUDs are affected by Atmosphere
if (no_atmo == 0)
{
vec3 sunlit;
vec3 amblit;
vec3 additive;
vec3 atten;
vec3 pos = vary_position.xyz/vary_position.w;
vec3 sunlit;
vec3 amblit;
vec3 additive;
vec3 atten;
vec3 pos = vary_position;
calcAtmosphericVars(pos.xyz, vec3(0), 1.0, sunlit, amblit, additive, atten, false);
calcAtmosphericVars(pos.xyz, vec3(0), 1.0, sunlit, amblit, additive, atten, false);
vec3 envColor = textureCube(environmentMap, vary_texcoord1.xyz).rgb;
float env_intensity = vertex_color.a;
//color.rgb = srgb_to_linear(color.rgb);
color.rgb = mix(color.rgb, envColor.rgb, env_intensity);
color.rgb = fullbrightAtmosTransportFrag(color.rgb, additive, atten);
color.rgb = fullbrightScaleSoftClip(color.rgb);
float env_intensity = vertex_color.a;
#ifndef HAS_REFLECTION_PROBES
vec3 envColor = textureCube(environmentMap, vary_texcoord1.xyz).rgb;
color.rgb = mix(color.rgb, envColor.rgb, env_intensity);
#else
vec3 ambenv;
vec3 glossenv;
vec3 legacyenv;
vec3 norm = normalize(vary_texcoord1.xyz);
vec4 spec = vec4(0,0,0,0);
sampleReflectionProbes(ambenv, glossenv, legacyenv, pos.xyz, norm.xyz, spec.a, env_intensity);
legacyenv *= 1.5; // fudge brighter
applyLegacyEnv(color.rgb, legacyenv, spec, pos, norm, env_intensity);
#endif
color.rgb = fullbrightAtmosTransportFrag(color.rgb, additive, atten);
color.rgb = fullbrightScaleSoftClip(color.rgb);
}
/*
@ -98,7 +115,6 @@ void main()
*/
color.a = 1.0;
//color.rgb = linear_to_srgb(color.rgb);
frag_color = color;

View File

@ -44,7 +44,7 @@ ATTRIBUTE vec2 texcoord0;
VARYING vec4 vertex_color;
VARYING vec2 vary_texcoord0;
VARYING vec3 vary_texcoord1;
VARYING vec4 vary_position;
VARYING vec3 vary_position;
#ifdef HAS_SKIN
mat4 getObjectSkinnedTransform();
@ -61,17 +61,23 @@ void main()
mat4 mat = getObjectSkinnedTransform();
mat = modelview_matrix * mat;
vec4 pos = mat * vert;
vary_position = gl_Position = projection_matrix * pos;
gl_Position = projection_matrix * pos;
vec3 norm = normalize((mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz);
#else
vec4 pos = (modelview_matrix * vert);
vary_position = gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
vec3 norm = normalize(normal_matrix * normal);
#endif
vec3 ref = reflect(pos.xyz, -norm);
vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
vary_position = pos.xyz;
vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
#ifndef HAS_REFLECTION_PROBES
vec3 ref = reflect(pos.xyz, -norm);
vary_texcoord1 = transpose(normal_matrix) * ref.xyz;
#else
vary_texcoord1 = norm;
#endif
calcAtmospherics(pos.xyz);

View File

@ -64,6 +64,13 @@ out vec4 frag_color;
float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen);
#endif
#ifdef HAS_REFLECTION_PROBES
void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv,
vec3 pos, vec3 norm, float glossiness, float envIntensity);
void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm);
void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity);
#endif
uniform samplerCube environmentMap;
uniform sampler2D lightFunc;
@ -322,6 +329,16 @@ void main()
// lighting from the sun stays sharp
float da = clamp(dot(normalize(norm.xyz), light_dir.xyz), 0.0, 1.0);
da = pow(da, 1.0 / 1.3);
vec3 sun_contrib = min(da, shadow) * sunlit;
#ifdef HAS_REFLECTION_PROBES
vec3 ambenv;
vec3 glossenv;
vec3 legacyenv;
sampleReflectionProbes(ambenv, glossenv, legacyenv, pos.xyz, norm.xyz, spec.a, envIntensity);
amblit = max(ambenv, amblit);
color.rgb = amblit;
#else
color = amblit;
@ -333,9 +350,8 @@ void main()
ambient *= ambient;
ambient = (1.0 - ambient);
vec3 sun_contrib = min(da, shadow) * sunlit;
color *= ambient;
#endif
color += sun_contrib;
@ -345,35 +361,6 @@ void main()
if (spec.a > 0.0) // specular reflection
{
/* // Reverting this specular calculation to previous 'dumbshiny' version - DJH 6/17/2020
// Preserving the refactored version as a comment for potential reconsideration,
// overriding the general rule to avoid pollutiong the source with commented code.
//
// If you're reading this in 2021+, feel free to obliterate.
vec3 npos = -normalize(pos.xyz);
//vec3 ref = dot(pos+lv, norm);
vec3 h = normalize(light_dir.xyz + npos);
float nh = dot(norm.xyz, h);
float nv = dot(norm.xyz, npos);
float vh = dot(npos, h);
float sa = nh;
float fres = pow(1 - dot(h, npos), 5)*0.4 + 0.5;
float gtdenom = 2 * nh;
float gt = max(0, min(gtdenom * nv / vh, gtdenom * da / vh));
if (nh > 0.0)
{
float scol = fres*texture2D(lightFunc, vec2(nh, spec.a)).r*gt / (nh*da);
vec3 sp = sun_contrib*scol / 6.0f;
sp = clamp(sp, vec3(0), vec3(1));
bloom = dot(sp, sp) / 4.0;
color += sp * spec.rgb;
}
*/
float sa = dot(refnormpersp, sun_dir.xyz);
vec3 dumbshiny = sunlit * shadow * (texture2D(lightFunc, vec2(sa, spec.a)).r);
@ -385,10 +372,24 @@ void main()
glare = max(glare, spec_contrib.b);
color += spec_contrib;
#ifdef HAS_REFLECTION_PROBES
applyGlossEnv(color, glossenv, spec, pos.xyz, norm.xyz);
#endif
}
color = mix(color.rgb, diffcol.rgb, diffuse.a);
#ifdef HAS_REFLECTION_PROBES
if (envIntensity > 0.0)
{ // add environmentmap
//fudge darker
legacyenv *= 0.5*diffuse.a+0.5;
applyLegacyEnv(color, legacyenv, spec, pos.xyz, norm.xyz, envIntensity);
}
#else
if (envIntensity > 0.0)
{
//add environmentmap
@ -403,6 +404,7 @@ void main()
cur_glare *= envIntensity*4.0;
glare += cur_glare;
}
#endif
color = atmosFragLighting(color, additive, atten);
color = scaleSoftClipFrag(color);

View File

@ -0,0 +1,44 @@
/**
* @file class1/deferred/reflectionProbeF.glsl
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2022, 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$
*/
// fallback stub -- will be used if actual reflection probe shader failed to load (output pink so it's obvious)
void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv,
vec3 pos, vec3 norm, float glossiness, float envIntensity)
{
ambenv = vec3(1,0,1);
glossenv = vec3(1,0,1);
legacyenv = vec3(1,0,1);
}
void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm)
{
color = vec3(1,0,1);
}
void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity)
{
color = vec3(1,0,1);
}

View File

@ -0,0 +1,476 @@
/**
* @file class2/deferred/reflectionProbeF.glsl
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2022, 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$
*/
#extension GL_ARB_shader_texture_lod : enable
#define FLT_MAX 3.402823466e+38
#define REFMAP_COUNT 256
#define REF_SAMPLE_COUNT 64 //maximum number of samples to consider
uniform samplerCubeArray reflectionProbes;
layout (std140, binding = 1) uniform ReflectionProbes
{
// list of OBBs for user override probes
// box is a set of 3 planes outward facing planes and the depth of the box along that plane
// for each box refBox[i]...
/// box[0..2] - plane 0 .. 2 in [A,B,C,D] notation
// box[3][0..2] - plane thickness
mat4 refBox[REFMAP_COUNT];
// list of bounding spheres for reflection probes sorted by distance to camera (closest first)
vec4 refSphere[REFMAP_COUNT];
// index of cube map in reflectionProbes for a corresponding reflection probe
// e.g. cube map channel of refSphere[2] is stored in refIndex[2]
// refIndex.x - cubemap channel in reflectionProbes
// refIndex.y - index in refNeighbor of neighbor list (index is ivec4 index, not int index)
// refIndex.z - number of neighbors
// refIndex.w - priority, if negative, this probe has a box influence
ivec4 refIndex[REFMAP_COUNT];
// neighbor list data (refSphere indices, not cubemap array layer)
ivec4 refNeighbor[1024];
// number of reflection probes present in refSphere
int refmapCount;
// intensity of ambient light from reflection probes
float reflectionAmbiance;
};
// Inputs
uniform mat3 env_mat;
// list of probeIndexes shader will actually use after "getRefIndex" is called
// (stores refIndex/refSphere indices, NOT rerflectionProbes layer)
int probeIndex[REF_SAMPLE_COUNT];
// number of probes stored in probeIndex
int probeInfluences = 0;
bool isAbove(vec3 pos, vec4 plane)
{
return (dot(plane.xyz, pos) + plane.w) > 0;
}
// return true if probe at index i influences position pos
bool shouldSampleProbe(int i, vec3 pos)
{
if (refIndex[i].w < 0)
{
vec4 v = refBox[i] * vec4(pos, 1.0);
if (abs(v.x) > 1 ||
abs(v.y) > 1 ||
abs(v.z) > 1)
{
return false;
}
}
else
{
vec3 delta = pos.xyz - refSphere[i].xyz;
float d = dot(delta, delta);
float r2 = refSphere[i].w;
r2 *= r2;
if (d > r2)
{ //outside bounding sphere
return false;
}
}
return true;
}
// call before sampleRef
// populate "probeIndex" with N probe indices that influence pos where N is REF_SAMPLE_COUNT
// overall algorithm --
void preProbeSample(vec3 pos)
{
// TODO: make some sort of structure that reduces the number of distance checks
for (int i = 0; i < refmapCount; ++i)
{
// found an influencing probe
if (shouldSampleProbe(i, pos))
{
probeIndex[probeInfluences] = i;
++probeInfluences;
int neighborIdx = refIndex[i].y;
if (neighborIdx != -1)
{
int neighborCount = min(refIndex[i].z, REF_SAMPLE_COUNT-1);
int count = 0;
while (count < neighborCount)
{
// check up to REF_SAMPLE_COUNT-1 neighbors (neighborIdx is ivec4 index)
int idx = refNeighbor[neighborIdx].x;
if (shouldSampleProbe(idx, pos))
{
probeIndex[probeInfluences++] = idx;
if (probeInfluences == REF_SAMPLE_COUNT)
{
return;
}
}
count++;
if (count == neighborCount)
{
return;
}
idx = refNeighbor[neighborIdx].y;
if (shouldSampleProbe(idx, pos))
{
probeIndex[probeInfluences++] = idx;
if (probeInfluences == REF_SAMPLE_COUNT)
{
return;
}
}
count++;
if (count == neighborCount)
{
return;
}
idx = refNeighbor[neighborIdx].z;
if (shouldSampleProbe(idx, pos))
{
probeIndex[probeInfluences++] = idx;
if (probeInfluences == REF_SAMPLE_COUNT)
{
return;
}
}
count++;
if (count == neighborCount)
{
return;
}
idx = refNeighbor[neighborIdx].w;
if (shouldSampleProbe(idx, pos))
{
probeIndex[probeInfluences++] = idx;
if (probeInfluences == REF_SAMPLE_COUNT)
{
return;
}
}
count++;
if (count == neighborCount)
{
return;
}
++neighborIdx;
}
return;
}
}
}
}
// from https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection
// original reference implementation:
/*
bool intersect(const Ray &ray) const
{
float t0, t1; // solutions for t if the ray intersects
#if 0
// geometric solution
Vec3f L = center - orig;
float tca = L.dotProduct(dir);
// if (tca < 0) return false;
float d2 = L.dotProduct(L) - tca * tca;
if (d2 > radius2) return false;
float thc = sqrt(radius2 - d2);
t0 = tca - thc;
t1 = tca + thc;
#else
// analytic solution
Vec3f L = orig - center;
float a = dir.dotProduct(dir);
float b = 2 * dir.dotProduct(L);
float c = L.dotProduct(L) - radius2;
if (!solveQuadratic(a, b, c, t0, t1)) return false;
#endif
if (t0 > t1) std::swap(t0, t1);
if (t0 < 0) {
t0 = t1; // if t0 is negative, let's use t1 instead
if (t0 < 0) return false; // both t0 and t1 are negative
}
t = t0;
return true;
} */
// adapted -- assume that origin is inside sphere, return distance from origin to edge of sphere
vec3 sphereIntersect(vec3 origin, vec3 dir, vec3 center, float radius2)
{
float t0, t1; // solutions for t if the ray intersects
vec3 L = center - origin;
float tca = dot(L,dir);
float d2 = dot(L,L) - tca * tca;
float thc = sqrt(radius2 - d2);
t0 = tca - thc;
t1 = tca + thc;
vec3 v = origin + dir * t1;
return v;
}
// from https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
/*
vec3 DirectionWS = normalize(PositionWS - CameraWS);
vec3 ReflDirectionWS = reflect(DirectionWS, NormalWS);
// Intersection with OBB convertto unit box space
// Transform in local unit parallax cube space (scaled and rotated)
vec3 RayLS = MulMatrix( float(3x3)WorldToLocal, ReflDirectionWS);
vec3 PositionLS = MulMatrix( WorldToLocal, PositionWS);
vec3 Unitary = vec3(1.0f, 1.0f, 1.0f);
vec3 FirstPlaneIntersect = (Unitary - PositionLS) / RayLS;
vec3 SecondPlaneIntersect = (-Unitary - PositionLS) / RayLS;
vec3 FurthestPlane = max(FirstPlaneIntersect, SecondPlaneIntersect);
float Distance = min(FurthestPlane.x, min(FurthestPlane.y, FurthestPlane.z));
// Use Distance in WS directly to recover intersection
vec3 IntersectPositionWS = PositionWS + ReflDirectionWS * Distance;
vec3 ReflDirectionWS = IntersectPositionWS - CubemapPositionWS;
return texCUBE(envMap, ReflDirectionWS);
*/
// get point of intersection with given probe's box influence volume
// origin - ray origin in clip space
// dir - ray direction in clip space
// i - probe index in refBox/refSphere
vec3 boxIntersect(vec3 origin, vec3 dir, int i)
{
// Intersection with OBB convertto unit box space
// Transform in local unit parallax cube space (scaled and rotated)
mat4 clipToLocal = refBox[i];
vec3 RayLS = mat3(clipToLocal) * dir;
vec3 PositionLS = (clipToLocal * vec4(origin, 1.0)).xyz;
vec3 Unitary = vec3(1.0f, 1.0f, 1.0f);
vec3 FirstPlaneIntersect = (Unitary - PositionLS) / RayLS;
vec3 SecondPlaneIntersect = (-Unitary - PositionLS) / RayLS;
vec3 FurthestPlane = max(FirstPlaneIntersect, SecondPlaneIntersect);
float Distance = min(FurthestPlane.x, min(FurthestPlane.y, FurthestPlane.z));
// Use Distance in CS directly to recover intersection
vec3 IntersectPositionCS = origin + dir * Distance;
return IntersectPositionCS;
}
// Tap a sphere based reflection probe
// pos - position of pixel
// dir - pixel normal
// lod - which mip to bias towards (lower is higher res, sharper reflections)
// c - center of probe
// r2 - radius of probe squared
// i - index of probe
// vi - point at which reflection vector struck the influence volume, in clip space
vec3 tapRefMap(vec3 pos, vec3 dir, float lod, vec3 c, float r2, int i)
{
//lod = max(lod, 1);
// parallax adjustment
vec3 v;
if (refIndex[i].w < 0)
{
v = boxIntersect(pos, dir, i);
}
else
{
v = sphereIntersect(pos, dir, c, r2);
}
v -= c;
v = env_mat * v;
{
float min_lod = textureQueryLod(reflectionProbes,v).y; // lower is higher res
return textureLod(reflectionProbes, vec4(v.xyz, refIndex[i].x), max(min_lod, lod)).rgb;
//return texture(reflectionProbes, vec4(v.xyz, refIndex[i].x)).rgb;
}
}
vec3 sampleProbes(vec3 pos, vec3 dir, float lod)
{
float wsum = 0.0;
vec3 col = vec3(0,0,0);
float vd2 = dot(pos,pos); // view distance squared
for (int idx = 0; idx < probeInfluences; ++idx)
{
int i = probeIndex[idx];
float r = refSphere[i].w; // radius of sphere volume
float p = float(abs(refIndex[i].w)); // priority
float rr = r*r; // radius squred
float r1 = r * 0.1; // 75% of radius (outer sphere to start interpolating down)
vec3 delta = pos.xyz-refSphere[i].xyz;
float d2 = dot(delta,delta);
float r2 = r1*r1;
{
vec3 refcol = tapRefMap(pos, dir, lod, refSphere[i].xyz, rr, i);
float w = 1.0/d2;
float atten = 1.0-max(d2-r2, 0.0)/(rr-r2);
w *= atten;
w *= p; // boost weight based on priority
col += refcol*w;
wsum += w;
}
}
if (probeInfluences <= 1)
{ //edge-of-scene probe or no probe influence, mix in with embiggened version of probes closest to camera
for (int idx = 0; idx < 8; ++idx)
{
if (refIndex[idx].w < 0)
{ // don't fallback to box probes, they are *very* specific
continue;
}
int i = idx;
vec3 delta = pos.xyz-refSphere[i].xyz;
float d2 = dot(delta,delta);
{
vec3 refcol = tapRefMap(pos, dir, lod, refSphere[i].xyz, d2, i);
float w = 1.0/d2;
w *= w;
col += refcol*w;
wsum += w;
}
}
}
if (wsum > 0.0)
{
col *= 1.0/wsum;
}
return col;
}
vec3 sampleProbeAmbient(vec3 pos, vec3 dir, float lod)
{
vec3 col = sampleProbes(pos, dir, lod);
//desaturate
vec3 hcol = col *0.5;
col *= 2.0;
col = vec3(
col.r + hcol.g + hcol.b,
col.g + hcol.r + hcol.b,
col.b + hcol.r + hcol.g
);
col *= 0.333333;
return col*reflectionAmbiance;
}
// brighten a color so that at least one component is 1
vec3 brighten(vec3 c)
{
float m = max(max(c.r, c.g), c.b);
if (m == 0)
{
return vec3(1,1,1);
}
return c * 1.0/m;
}
void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv,
vec3 pos, vec3 norm, float glossiness, float envIntensity)
{
// TODO - don't hard code lods
float reflection_lods = 8;
preProbeSample(pos);
vec3 refnormpersp = reflect(pos.xyz, norm.xyz);
ambenv = sampleProbeAmbient(pos, norm, reflection_lods-1);
if (glossiness > 0.0)
{
float lod = (1.0-glossiness)*reflection_lods;
glossenv = sampleProbes(pos, normalize(refnormpersp), lod);
}
if (envIntensity > 0.0)
{
legacyenv = sampleProbes(pos, normalize(refnormpersp), 0.0);
}
}
void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm)
{
glossenv *= 0.35; // fudge darker
float fresnel = 1.0+dot(normalize(pos.xyz), norm.xyz);
float minf = spec.a * 0.1;
fresnel = fresnel * (1.0-minf) + minf;
glossenv *= spec.rgb*min(fresnel, 1.0);
color.rgb += glossenv;
}
void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity)
{
vec3 reflected_color = legacyenv; //*0.5; //fudge darker
vec3 lookAt = normalize(pos);
float fresnel = 1.0+dot(lookAt, norm.xyz);
fresnel *= fresnel;
fresnel = min(fresnel+envIntensity, 1.0);
reflected_color *= (envIntensity*fresnel)*brighten(spec.rgb);
color = mix(color.rgb, reflected_color, envIntensity);
}

View File

@ -42,38 +42,8 @@ uniform sampler2DRect specularRect;
uniform sampler2DRect normalMap;
uniform sampler2DRect lightMap;
uniform sampler2DRect depthMap;
uniform samplerCube environmentMap;
uniform samplerCubeArray reflectionProbes;
uniform sampler2D lightFunc;
layout (std140, binding = 1) uniform ReflectionProbes
{
// list of OBBs for user override probes
// box is a set of 3 planes outward facing planes and the depth of the box along that plane
// for each box refBox[i]...
/// box[0..2] - plane 0 .. 2 in [A,B,C,D] notation
// box[3][0..2] - plane thickness
mat4 refBox[REFMAP_COUNT];
// list of bounding spheres for reflection probes sorted by distance to camera (closest first)
vec4 refSphere[REFMAP_COUNT];
// index of cube map in reflectionProbes for a corresponding reflection probe
// e.g. cube map channel of refSphere[2] is stored in refIndex[2]
// refIndex.x - cubemap channel in reflectionProbes
// refIndex.y - index in refNeighbor of neighbor list (index is ivec4 index, not int index)
// refIndex.z - number of neighbors
// refIndex.w - priority, if negative, this probe has a box influence
ivec4 refIndex[REFMAP_COUNT];
// neighbor list data (refSphere indices, not cubemap array layer)
ivec4 refNeighbor[1024];
// number of reflection probes present in refSphere
int refmapCount;
// intensity of ambient light from reflection probes
float reflectionAmbiance;
};
uniform float blur_size;
uniform float blur_fidelity;
@ -98,6 +68,12 @@ vec3 scaleSoftClipFrag(vec3 l);
vec3 fullbrightAtmosTransportFrag(vec3 light, vec3 additive, vec3 atten);
vec3 fullbrightScaleSoftClip(vec3 light);
// reflection probe interface
void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyEnv,
vec3 pos, vec3 norm, float glossiness, float envIntensity);
void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm);
void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity);
vec3 linear_to_srgb(vec3 c);
vec3 srgb_to_linear(vec3 c);
@ -105,376 +81,8 @@ vec3 srgb_to_linear(vec3 c);
vec4 applyWaterFogView(vec3 pos, vec4 color);
#endif
// list of probeIndexes shader will actually use after "getRefIndex" is called
// (stores refIndex/refSphere indices, NOT rerflectionProbes layer)
int probeIndex[REF_SAMPLE_COUNT];
// number of probes stored in probeIndex
int probeInfluences = 0;
bool isAbove(vec3 pos, vec4 plane)
{
return (dot(plane.xyz, pos) + plane.w) > 0;
}
// return true if probe at index i influences position pos
bool shouldSampleProbe(int i, vec3 pos)
{
if (refIndex[i].w < 0)
{
vec4 v = refBox[i] * vec4(pos, 1.0);
if (abs(v.x) > 1 ||
abs(v.y) > 1 ||
abs(v.z) > 1)
{
return false;
}
}
else
{
vec3 delta = pos.xyz - refSphere[i].xyz;
float d = dot(delta, delta);
float r2 = refSphere[i].w;
r2 *= r2;
if (d > r2)
{ //outside bounding sphere
return false;
}
}
return true;
}
// populate "probeIndex" with N probe indices that influence pos where N is REF_SAMPLE_COUNT
// overall algorithm --
void getRefIndex(vec3 pos)
{
// TODO: make some sort of structure that reduces the number of distance checks
for (int i = 0; i < refmapCount; ++i)
{
// found an influencing probe
if (shouldSampleProbe(i, pos))
{
probeIndex[probeInfluences] = i;
++probeInfluences;
int neighborIdx = refIndex[i].y;
if (neighborIdx != -1)
{
int neighborCount = min(refIndex[i].z, REF_SAMPLE_COUNT-1);
int count = 0;
while (count < neighborCount)
{
// check up to REF_SAMPLE_COUNT-1 neighbors (neighborIdx is ivec4 index)
int idx = refNeighbor[neighborIdx].x;
if (shouldSampleProbe(idx, pos))
{
probeIndex[probeInfluences++] = idx;
if (probeInfluences == REF_SAMPLE_COUNT)
{
return;
}
}
count++;
if (count == neighborCount)
{
return;
}
idx = refNeighbor[neighborIdx].y;
if (shouldSampleProbe(idx, pos))
{
probeIndex[probeInfluences++] = idx;
if (probeInfluences == REF_SAMPLE_COUNT)
{
return;
}
}
count++;
if (count == neighborCount)
{
return;
}
idx = refNeighbor[neighborIdx].z;
if (shouldSampleProbe(idx, pos))
{
probeIndex[probeInfluences++] = idx;
if (probeInfluences == REF_SAMPLE_COUNT)
{
return;
}
}
count++;
if (count == neighborCount)
{
return;
}
idx = refNeighbor[neighborIdx].w;
if (shouldSampleProbe(idx, pos))
{
probeIndex[probeInfluences++] = idx;
if (probeInfluences == REF_SAMPLE_COUNT)
{
return;
}
}
count++;
if (count == neighborCount)
{
return;
}
++neighborIdx;
}
return;
}
}
}
}
// from https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection
// original reference implementation:
/*
bool intersect(const Ray &ray) const
{
float t0, t1; // solutions for t if the ray intersects
#if 0
// geometric solution
Vec3f L = center - orig;
float tca = L.dotProduct(dir);
// if (tca < 0) return false;
float d2 = L.dotProduct(L) - tca * tca;
if (d2 > radius2) return false;
float thc = sqrt(radius2 - d2);
t0 = tca - thc;
t1 = tca + thc;
#else
// analytic solution
Vec3f L = orig - center;
float a = dir.dotProduct(dir);
float b = 2 * dir.dotProduct(L);
float c = L.dotProduct(L) - radius2;
if (!solveQuadratic(a, b, c, t0, t1)) return false;
#endif
if (t0 > t1) std::swap(t0, t1);
if (t0 < 0) {
t0 = t1; // if t0 is negative, let's use t1 instead
if (t0 < 0) return false; // both t0 and t1 are negative
}
t = t0;
return true;
} */
// adapted -- assume that origin is inside sphere, return distance from origin to edge of sphere
vec3 sphereIntersect(vec3 origin, vec3 dir, vec3 center, float radius2)
{
float t0, t1; // solutions for t if the ray intersects
vec3 L = center - origin;
float tca = dot(L,dir);
float d2 = dot(L,L) - tca * tca;
float thc = sqrt(radius2 - d2);
t0 = tca - thc;
t1 = tca + thc;
vec3 v = origin + dir * t1;
return v;
}
// from https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
/*
vec3 DirectionWS = normalize(PositionWS - CameraWS);
vec3 ReflDirectionWS = reflect(DirectionWS, NormalWS);
// Intersection with OBB convertto unit box space
// Transform in local unit parallax cube space (scaled and rotated)
vec3 RayLS = MulMatrix( float(3x3)WorldToLocal, ReflDirectionWS);
vec3 PositionLS = MulMatrix( WorldToLocal, PositionWS);
vec3 Unitary = vec3(1.0f, 1.0f, 1.0f);
vec3 FirstPlaneIntersect = (Unitary - PositionLS) / RayLS;
vec3 SecondPlaneIntersect = (-Unitary - PositionLS) / RayLS;
vec3 FurthestPlane = max(FirstPlaneIntersect, SecondPlaneIntersect);
float Distance = min(FurthestPlane.x, min(FurthestPlane.y, FurthestPlane.z));
// Use Distance in WS directly to recover intersection
vec3 IntersectPositionWS = PositionWS + ReflDirectionWS * Distance;
vec3 ReflDirectionWS = IntersectPositionWS - CubemapPositionWS;
return texCUBE(envMap, ReflDirectionWS);
*/
// get point of intersection with given probe's box influence volume
// origin - ray origin in clip space
// dir - ray direction in clip space
// i - probe index in refBox/refSphere
vec3 boxIntersect(vec3 origin, vec3 dir, int i)
{
// Intersection with OBB convertto unit box space
// Transform in local unit parallax cube space (scaled and rotated)
mat4 clipToLocal = refBox[i];
vec3 RayLS = mat3(clipToLocal) * dir;
vec3 PositionLS = (clipToLocal * vec4(origin, 1.0)).xyz;
vec3 Unitary = vec3(1.0f, 1.0f, 1.0f);
vec3 FirstPlaneIntersect = (Unitary - PositionLS) / RayLS;
vec3 SecondPlaneIntersect = (-Unitary - PositionLS) / RayLS;
vec3 FurthestPlane = max(FirstPlaneIntersect, SecondPlaneIntersect);
float Distance = min(FurthestPlane.x, min(FurthestPlane.y, FurthestPlane.z));
// Use Distance in CS directly to recover intersection
vec3 IntersectPositionCS = origin + dir * Distance;
return IntersectPositionCS;
}
// Tap a sphere based reflection probe
// pos - position of pixel
// dir - pixel normal
// lod - which mip to bias towards (lower is higher res, sharper reflections)
// c - center of probe
// r2 - radius of probe squared
// i - index of probe
// vi - point at which reflection vector struck the influence volume, in clip space
vec3 tapRefMap(vec3 pos, vec3 dir, float lod, vec3 c, float r2, int i)
{
//lod = max(lod, 1);
// parallax adjustment
vec3 v;
if (refIndex[i].w < 0)
{
v = boxIntersect(pos, dir, i);
}
else
{
v = sphereIntersect(pos, dir, c, r2);
}
v -= c;
v = env_mat * v;
{
float min_lod = textureQueryLod(reflectionProbes,v).y; // lower is higher res
return textureLod(reflectionProbes, vec4(v.xyz, refIndex[i].x), max(min_lod, lod)).rgb;
//return texture(reflectionProbes, vec4(v.xyz, refIndex[i].x)).rgb;
}
}
vec3 sampleRefMap(vec3 pos, vec3 dir, float lod)
{
float wsum = 0.0;
vec3 col = vec3(0,0,0);
float vd2 = dot(pos,pos); // view distance squared
for (int idx = 0; idx < probeInfluences; ++idx)
{
int i = probeIndex[idx];
float r = refSphere[i].w; // radius of sphere volume
float p = float(abs(refIndex[i].w)); // priority
float rr = r*r; // radius squred
float r1 = r * 0.1; // 75% of radius (outer sphere to start interpolating down)
vec3 delta = pos.xyz-refSphere[i].xyz;
float d2 = dot(delta,delta);
float r2 = r1*r1;
{
vec3 refcol = tapRefMap(pos, dir, lod, refSphere[i].xyz, rr, i);
float w = 1.0/d2;
float atten = 1.0-max(d2-r2, 0.0)/(rr-r2);
w *= atten;
w *= p; // boost weight based on priority
col += refcol*w;
wsum += w;
}
}
if (probeInfluences <= 1)
{ //edge-of-scene probe or no probe influence, mix in with embiggened version of probes closest to camera
for (int idx = 0; idx < 8; ++idx)
{
if (refIndex[idx].w < 0)
{ // don't fallback to box probes, they are *very* specific
continue;
}
int i = idx;
vec3 delta = pos.xyz-refSphere[i].xyz;
float d2 = dot(delta,delta);
{
vec3 refcol = tapRefMap(pos, dir, lod, refSphere[i].xyz, d2, i);
float w = 1.0/d2;
w *= w;
col += refcol*w;
wsum += w;
}
}
}
if (wsum > 0.0)
{
col *= 1.0/wsum;
}
return col;
}
vec3 sampleAmbient(vec3 pos, vec3 dir, float lod)
{
vec3 col = sampleRefMap(pos, dir, lod);
//desaturate
vec3 hcol = col *0.5;
col *= 2.0;
col = vec3(
col.r + hcol.g + hcol.b,
col.g + hcol.r + hcol.b,
col.b + hcol.r + hcol.g
);
col *= 0.333333;
return col*reflectionAmbiance;
}
// brighten a color so that at least one component is 1
vec3 brighten(vec3 c)
{
float m = max(max(c.r, c.g), c.b);
if (m == 0)
{
return vec3(1,1,1);
}
return c * 1.0/m;
}
void main()
{
float reflection_lods = 8; // TODO -- base this on resolution of reflection map instead of hard coding
vec2 tc = vary_fragcoord.xy;
float depth = texture2DRect(depthMap, tc.xy).r;
vec4 pos = getPositionWithDepth(tc, depth);
@ -504,13 +112,15 @@ void main()
vec3 additive;
vec3 atten;
getRefIndex(pos.xyz);
calcAtmosphericVars(pos.xyz, light_dir, ambocc, sunlit, amblit, additive, atten, true);
//vec3 amb_vec = env_mat * norm.xyz;
vec3 ambenv = sampleAmbient(pos.xyz, norm.xyz, reflection_lods-1);
vec3 ambenv;
vec3 glossenv;
vec3 legacyenv;
sampleReflectionProbes(ambenv, glossenv, legacyenv, pos.xyz, norm.xyz, spec.a, envIntensity);
amblit = max(ambenv, amblit);
color.rgb = amblit*ambocc;
@ -527,7 +137,6 @@ void main()
vec3 refnormpersp = reflect(pos.xyz, norm.xyz);
vec3 env_vec = env_mat * refnormpersp;
if (spec.a > 0.0) // specular reflection
{
float sa = dot(normalize(refnormpersp), light_dir.xyz);
@ -539,27 +148,16 @@ void main()
color.rgb += spec_contrib;
// add reflection map - EXPERIMENTAL WORK IN PROGRESS
float lod = (1.0-spec.a)*reflection_lods;
vec3 reflected_color = sampleRefMap(pos.xyz, normalize(refnormpersp), lod);
reflected_color *= 0.35; // fudge darker
float fresnel = 1.0+dot(normalize(pos.xyz), norm.xyz);
float minf = spec.a * 0.1;
fresnel = fresnel * (1.0-minf) + minf;
reflected_color *= spec.rgb*min(fresnel, 1.0);
color.rgb += reflected_color;
applyGlossEnv(color, glossenv, spec, pos.xyz, norm.xyz);
}
color.rgb = mix(color.rgb, diffuse.rgb, diffuse.a);
if (envIntensity > 0.0)
{ // add environmentmap
vec3 reflected_color = sampleRefMap(pos.xyz, normalize(refnormpersp), 0.0)*0.5; //fudge darker
float fresnel = 1.0+dot(normalize(pos.xyz), norm.xyz);
fresnel *= fresnel;
fresnel = min(fresnel+envIntensity, 1.0);
reflected_color *= (envIntensity*fresnel)*brighten(spec.rgb);
color = mix(color.rgb, reflected_color, envIntensity);
//fudge darker
legacyenv *= 0.5*diffuse.a+0.5;;
applyLegacyEnv(color, legacyenv, spec, pos.xyz, norm.xyz, envIntensity);
}
if (norm.w < 0.5)
@ -577,6 +175,8 @@ void main()
// convert to linear as fullscreen lights need to sum in linear colorspace
// and will be gamma (re)corrected downstream...
//color = vec3(ambocc);
//color = ambenv;
//color = ambenv;
//color.b = diffuse.a;
frag_color.rgb = srgb_to_linear(color.rgb);
frag_color.a = bloom;
}

View File

@ -460,6 +460,11 @@ void LLDrawPoolBump::beginFullbrightShiny()
LLVector4 vec4(vec, gShinyOrigin.mV[3]);
shader->uniform4fv(LLViewerShaderMgr::SHINY_ORIGIN, 1, vec4.mV);
if (shader->mFeatures.hasReflectionProbes)
{
gPipeline.bindReflectionProbes(*shader);
}
// Make sure that texture coord generation happens for tex unit 1, as that's the one we use for
// the cube map in the one pass shiny shaders
gGL.getTexUnit(1)->disable();
@ -520,6 +525,10 @@ void LLDrawPoolBump::endFullbrightShiny()
if( cube_map )
{
cube_map->disable();
if (shader->mFeatures.hasReflectionProbes)
{
gPipeline.unbindReflectionProbes(*shader);
}
shader->unbind();
}

View File

@ -506,6 +506,8 @@ void LLReflectionMapManager::setUniforms()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
// TODO -- avoid repacking UBO unnecessarily
// structure for packing uniform buffer object
// see class2/deferred/softenLightF.glsl
struct ReflectionProbeData

View File

@ -977,6 +977,7 @@ BOOL LLViewerShaderMgr::loadBasicShaders()
index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/deferredUtil.glsl", 1) );
index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/shadowUtil.glsl", 1) );
index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/aoUtil.glsl", 1) );
index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/reflectionProbeF.glsl", llmax(mShaderLevel[SHADER_DEFERRED], 1)) );
index_channels.push_back(-1); shaders.push_back( make_pair( "lighting/lightNonIndexedF.glsl", mShaderLevel[SHADER_LIGHTING] ) );
index_channels.push_back(-1); shaders.push_back( make_pair( "lighting/lightAlphaMaskNonIndexedF.glsl", mShaderLevel[SHADER_LIGHTING] ) );
index_channels.push_back(-1); shaders.push_back( make_pair( "lighting/lightFullbrightNonIndexedF.glsl", mShaderLevel[SHADER_LIGHTING] ) );
@ -1503,6 +1504,12 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredMaterialProgram[i].mFeatures.hasGamma = true;
gDeferredMaterialProgram[i].mFeatures.hasShadows = use_sun_shadow;
if (mShaderLevel[SHADER_DEFERRED] > 1)
{
gDeferredMaterialProgram[i].mFeatures.hasReflectionProbes = true;
gDeferredMaterialProgram[i].addPermutation("HAS_REFLECTION_PROBES", "1");
}
if (has_skin)
{
gDeferredMaterialProgram[i].addPermutation("HAS_SKIN", "1");
@ -2202,6 +2209,11 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredFullbrightShinyProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightShinyV.glsl", GL_VERTEX_SHADER_ARB));
gDeferredFullbrightShinyProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightShinyF.glsl", GL_FRAGMENT_SHADER_ARB));
gDeferredFullbrightShinyProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
if (gDeferredFullbrightShinyProgram.mShaderLevel >= 2) // TODO : make this a 3 when reflection probes are restricted to class 3
{
gDeferredFullbrightShinyProgram.addPermutation("HAS_REFLECTION_PROBES", "1");
gDeferredFullbrightShinyProgram.mFeatures.hasReflectionProbes = true;
}
success = make_rigged_variant(gDeferredFullbrightShinyProgram, gDeferredSkinnedFullbrightShinyProgram);
success = success && gDeferredFullbrightShinyProgram.createShader(NULL, NULL);
llassert(success);
@ -2274,6 +2286,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredSoftenProgram.mFeatures.hasGamma = true;
gDeferredSoftenProgram.mFeatures.isDeferred = true;
gDeferredSoftenProgram.mFeatures.hasShadows = use_sun_shadow;
gDeferredSoftenProgram.mFeatures.hasReflectionProbes = true;
gDeferredSoftenProgram.clearPermutations();
gDeferredSoftenProgram.mShaderFiles.push_back(make_pair("deferred/softenLightV.glsl", GL_VERTEX_SHADER_ARB));
@ -2324,6 +2337,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredSoftenWaterProgram.mFeatures.hasGamma = true;
gDeferredSoftenWaterProgram.mFeatures.isDeferred = true;
gDeferredSoftenWaterProgram.mFeatures.hasShadows = use_sun_shadow;
gDeferredSoftenWaterProgram.mFeatures.hasReflectionProbes = true;
if (ambient_kill)
{

View File

@ -8208,30 +8208,16 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_
stop_glerror();
bool setup_env_mat = false;
channel = shader.enableTexture(LLShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
if (channel > -1)
{
LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
if (cube_map)
{
setup_env_mat = true;
cube_map->enable(channel);
cube_map->bind();
}
}
channel = shader.enableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP_ARRAY);
if (channel > -1 && mReflectionMapManager.mTexture.notNull())
{
// see comments in class2/deferred/softenLightF.glsl for what these uniforms mean
mReflectionMapManager.mTexture->bind(channel);
mReflectionMapManager.setUniforms();
setup_env_mat = true;
}
if (setup_env_mat)
{
F32* m = gGLModelView;
F32 mat[] = { m[0], m[1], m[2],
@ -8239,8 +8225,10 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_
m[8], m[9], m[10] };
shader.uniformMatrix3fv(LLShaderMgr::DEFERRED_ENV_MAT, 1, TRUE, mat);
}
}
bindReflectionProbes(shader);
if (gAtmosphere)
{
// bind precomputed textures necessary for calculating sun and sky luminance
@ -9163,7 +9151,35 @@ void LLPipeline::unbindDeferredShader(LLGLSLShader &shader)
}
}
channel = shader.disableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP);
unbindReflectionProbes(shader);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.getTexUnit(0)->activate();
shader.unbind();
}
void LLPipeline::bindReflectionProbes(LLGLSLShader& shader)
{
S32 channel = shader.enableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP_ARRAY);
if (channel > -1 && mReflectionMapManager.mTexture.notNull())
{
// see comments in class2/deferred/softenLightF.glsl for what these uniforms mean
mReflectionMapManager.mTexture->bind(channel);
mReflectionMapManager.setUniforms();
F32* m = gGLModelView;
F32 mat[] = { m[0], m[1], m[2],
m[4], m[5], m[6],
m[8], m[9], m[10] };
shader.uniformMatrix3fv(LLShaderMgr::DEFERRED_ENV_MAT, 1, TRUE, mat);
}
}
void LLPipeline::unbindReflectionProbes(LLGLSLShader& shader)
{
S32 channel = shader.disableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP);
if (channel > -1 && mReflectionMapManager.mTexture.notNull())
{
mReflectionMapManager.mTexture->unbind();
@ -9172,12 +9188,9 @@ void LLPipeline::unbindDeferredShader(LLGLSLShader &shader)
gGL.getTexUnit(channel)->enable(LLTexUnit::TT_TEXTURE);
}
}
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.getTexUnit(0)->activate();
shader.unbind();
}
inline float sgn(float a)
{
if (a > 0.0F) return (1.0F);

View File

@ -294,6 +294,10 @@ public:
void setupSpotLight(LLGLSLShader& shader, LLDrawable* drawablep);
void unbindDeferredShader(LLGLSLShader& shader);
void bindReflectionProbes(LLGLSLShader& shader);
void unbindReflectionProbes(LLGLSLShader& shader);
void renderDeferredLighting(LLRenderTarget* light_target);
void postDeferredGammaCorrect(LLRenderTarget* screen_target);