SL-17283 LLReflectionMapManager prototype. Remove snapshot code related overhead from reflection map renders. Add parallax correction and support for multiple reflection maps.

master
Dave Parks 2022-05-04 16:07:50 +00:00
parent 82311e4b44
commit 93260cfeff
18 changed files with 696 additions and 217 deletions

View File

@ -166,6 +166,19 @@ void LLCubeMap::init(const std::vector<LLPointer<LLImageRaw> >& rawimages)
}
}
void LLCubeMap::initReflectionMap(U32 resolution, U32 components)
{
U32 texname = 0;
LLImageGL::generateTextures(1, &texname);
mImages[0] = new LLImageGL(resolution, resolution, components, TRUE);
mImages[0]->setTexName(texname);
mImages[0]->setTarget(mTargets[0], LLTexUnit::TT_CUBE_MAP);
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname);
mImages[0]->setAddressMode(LLTexUnit::TAM_CLAMP);
}
void LLCubeMap::initEnvironmentMap(const std::vector<LLPointer<LLImageRaw> >& rawimages)
{
llassert(rawimages.size() == 6);
@ -203,6 +216,19 @@ void LLCubeMap::initEnvironmentMap(const std::vector<LLPointer<LLImageRaw> >& ra
disable();
}
void LLCubeMap::generateMipMaps()
{
mImages[0]->setUseMipMaps(TRUE);
mImages[0]->setHasMipMaps(TRUE);
enableTexture(0);
bind();
mImages[0]->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
gGL.getTexUnit(0)->disable();
disable();
}
GLuint LLCubeMap::getGLName()
{
return mImages[0]->getTexName();

View File

@ -41,11 +41,15 @@ public:
LLCubeMap(bool init_as_srgb);
void init(const std::vector<LLPointer<LLImageRaw> >& rawimages);
// initialize as an undefined cubemap at the given resolution
// used for render-to-cubemap operations
// avoids usage of LLImageRaw
void initReflectionMap(U32 resolution, U32 components = 3);
// init from environment map images
// Similar to init, but takes ownership of rawimages and makes this cubemap
// respect the resolution of rawimages
// Raw images must point to array of six square images that are all the same resolution
// For example usage, see LLEnvironmentMap
void initEnvironmentMap(const std::vector<LLPointer<LLImageRaw> >& rawimages);
void initGL();
void initRawData(const std::vector<LLPointer<LLImageRaw> >& rawimages);
@ -62,6 +66,12 @@ public:
void setMatrix(S32 stage);
void restoreMatrix();
U32 getResolution() { return mImages[0].notNull() ? mImages[0]->getWidth(0) : 0; }
// generate mip maps for this Cube Map using GL
// NOTE: Cube Map MUST already be resident in VRAM
void generateMipMaps();
GLuint getGLName();
void destroyGL();

View File

@ -738,7 +738,7 @@ void LLGLSLShader::mapUniform(GLint index, const vector<LLStaticHashedString> *
{
//found it
mUniform[i] = location;
mTexture[i] = mapUniformTextureChannel(location, type);
mTexture[i] = mapUniformTextureChannel(location, type, size);
return;
}
}
@ -752,7 +752,7 @@ void LLGLSLShader::mapUniform(GLint index, const vector<LLStaticHashedString> *
{
//found it
mUniform[i+LLShaderMgr::instance()->mReservedUniforms.size()] = location;
mTexture[i+LLShaderMgr::instance()->mReservedUniforms.size()] = mapUniformTextureChannel(location, type);
mTexture[i+LLShaderMgr::instance()->mReservedUniforms.size()] = mapUniformTextureChannel(location, type, size);
return;
}
}
@ -775,16 +775,37 @@ void LLGLSLShader::removePermutation(std::string name)
mDefines[name].erase();
}
GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type)
GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type, GLint size)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
if ((type >= GL_SAMPLER_1D_ARB && type <= GL_SAMPLER_2D_RECT_SHADOW_ARB) ||
type == GL_SAMPLER_2D_MULTISAMPLE)
{ //this here is a texture
glUniform1iARB(location, mActiveTextureChannels);
LL_DEBUGS("ShaderUniform") << "Assigned to texture channel " << mActiveTextureChannels << LL_ENDL;
return mActiveTextureChannels++;
GLint ret = mActiveTextureChannels;
if (size == 1)
{
glUniform1iARB(location, mActiveTextureChannels);
LL_DEBUGS("ShaderUniform") << "Assigned to texture channel " << mActiveTextureChannels << LL_ENDL;
mActiveTextureChannels++;
}
else
{
//is array of textures, make sequential after this texture
GLint channel[32]; // <=== only support up to 32 texture channels
llassert(size <= 32);
size = llmin(size, 32);
for (int i = 0; i < size; ++i)
{
channel[i] = mActiveTextureChannels++;
}
glUniform1ivARB(location, size, channel);
LL_DEBUGS("ShaderUniform") << "Assigned to texture channel " <<
(mActiveTextureChannels-size) << " through " << (mActiveTextureChannels-1) << LL_ENDL;
}
llassert(mActiveTextureChannels <= 32); // too many textures (probably)
return ret;
}
return -1;
}

View File

@ -207,7 +207,7 @@ public:
GLint getUniformLocation(U32 index);
GLint getAttribLocation(U32 attrib);
GLint mapUniformTextureChannel(GLint location, GLenum type);
GLint mapUniformTextureChannel(GLint location, GLenum type, GLint size);
void clearPermutations();
void addPermutation(std::string name, std::string value);

View File

@ -161,7 +161,7 @@ public:
BOOL getUseMipMaps() const { return mUseMipMaps; }
void setUseMipMaps(BOOL usemips) { mUseMipMaps = usemips; }
void setHasMipMaps(BOOL hasmips) { mHasMipMaps = hasmips; }
void updatePickMask(S32 width, S32 height, const U8* data_in);
BOOL getMask(const LLVector2 &tc);

View File

@ -119,6 +119,7 @@ void LLRenderTarget::resize(U32 resx, U32 resy)
bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, S32 samples)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
resx = llmin(resx, (U32) gGLManager.mGLMaxTextureSize);
resy = llmin(resy, (U32) gGLManager.mGLMaxTextureSize);
@ -219,6 +220,7 @@ void LLRenderTarget::releaseColorAttachment()
bool LLRenderTarget::addColorAttachment(U32 color_fmt)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
if (color_fmt == 0)
{
return true;
@ -315,6 +317,7 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt)
bool LLRenderTarget::allocateDepth()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
if (mStencil)
{
//use render buffers where stencil buffers are in play
@ -395,6 +398,7 @@ void LLRenderTarget::shareDepthBuffer(LLRenderTarget& target)
void LLRenderTarget::release()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
if (mDepth)
{
if (mStencil)

View File

@ -196,7 +196,6 @@ set(viewer_SOURCE_FILES
lldynamictexture.cpp
llemote.cpp
llenvironment.cpp
llenvironmentmap.cpp
llestateinfomodel.cpp
lleventnotifier.cpp
lleventpoll.cpp
@ -546,6 +545,8 @@ set(viewer_SOURCE_FILES
llproductinforequest.cpp
llprogressview.cpp
llrecentpeople.cpp
llreflectionmap.cpp
llreflectionmapmanager.cpp
llregioninfomodel.cpp
llregionposition.cpp
llremoteparcelrequest.cpp
@ -833,7 +834,6 @@ set(viewer_HEADER_FILES
lldynamictexture.h
llemote.h
llenvironment.h
llenvironmentmap.h
llestateinfomodel.h
lleventnotifier.h
lleventpoll.h
@ -1171,6 +1171,8 @@ set(viewer_HEADER_FILES
llproductinforequest.h
llprogressview.h
llrecentpeople.h
llreflectionmap.h
llreflectionmapmanager.h
llregioninfomodel.h
llregionposition.h
llremoteparcelrequest.h

View File

@ -28,6 +28,8 @@
/*[EXTRA_CODE_HERE]*/
#define REFMAP_COUNT 8
#ifdef DEFINE_GL_FRAGCOLOR
out vec4 frag_color;
#else
@ -40,9 +42,13 @@ uniform sampler2DRect normalMap;
uniform sampler2DRect lightMap;
uniform sampler2DRect depthMap;
uniform samplerCube environmentMap;
uniform samplerCube reflectionMap;
uniform samplerCube reflectionMap[REFMAP_COUNT];
uniform sampler2D lightFunc;
uniform int refmapCount;
uniform vec3 refOrigin[REFMAP_COUNT];
uniform float blur_size;
uniform float blur_fidelity;
@ -74,8 +80,136 @@ vec3 srgb_to_linear(vec3 c);
vec4 applyWaterFogView(vec3 pos, vec4 color);
#endif
// 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
float sphereIntersect(vec3 origin, vec3 dir, vec4 sph )
{
float t0, t1; // solutions for t if the ray intersects
vec3 center = sph.xyz;
float radius2 = sph.w * sph.w;
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;
return t1;
}
vec3 sampleRefMap(vec3 pos, vec3 dir, float lod)
{
float wsum = 0.0;
vec3 col = vec3(0,0,0);
for (int i = 0; i < refmapCount; ++i)
//int i = 0;
{
float r = 16.0;
vec3 delta = pos.xyz-refOrigin[i].xyz;
if (length(delta) < r)
{
float w = 1.0/max(dot(delta, delta), r);
w *= w;
w *= w;
// parallax adjustment
float d = sphereIntersect(pos, dir, vec4(refOrigin[i].xyz, r));
{
vec3 v = pos + dir * d;
v -= refOrigin[i].xyz;
v = env_mat * v;
float min_lod = textureQueryLod(reflectionMap[i],v).y; // lower is higher res
col += textureLod(reflectionMap[i], v, max(min_lod, lod)).rgb*w;
wsum += w;
}
}
}
if (wsum > 0.0)
{
col *= 1.0/wsum;
}
else
{
// this pixel not covered by a probe, fallback to "full scene" environment map
vec3 v = env_mat * dir;
float min_lod = textureQueryLod(environmentMap, v).y; // lower is higher res
col = textureLod(environmentMap, v, max(min_lod, lod)).rgb;
}
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*0.6; // fudge darker
}
void main()
{
float reflection_lods = 11; // 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);
@ -106,25 +240,29 @@ void main()
vec3 atten;
calcAtmosphericVars(pos.xyz, light_dir, ambocc, sunlit, amblit, additive, atten, true);
color.rgb = amblit;
//vec3 amb_vec = env_mat * norm.xyz;
float ambient = min(abs(dot(norm.xyz, sun_dir.xyz)), 1.0);
ambient *= 0.5;
ambient *= ambient;
ambient = (1.0 - ambient);
color.rgb *= ambient;
vec3 ambenv = sampleAmbient(pos.xyz, norm.xyz, reflection_lods-1);
amblit = mix(ambenv, amblit, amblit);
color.rgb = amblit;
//float ambient = min(abs(dot(norm.xyz, sun_dir.xyz)), 1.0);
//ambient *= 0.5;
//ambient *= ambient;
//ambient = (1.0 - ambient);
//color.rgb *= ambient;
vec3 sun_contrib = min(da, scol) * sunlit;
color.rgb += sun_contrib;
color.rgb *= diffuse.rgb;
vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz));
vec3 refnormpersp = reflect(pos.xyz, norm.xyz);
vec3 env_vec = env_mat * refnormpersp;
if (spec.a > 0.0) // specular reflection
{
float sa = dot(refnormpersp, light_dir.xyz);
float sa = dot(normalize(refnormpersp), light_dir.xyz);
vec3 dumbshiny = sunlit * scol * (texture2D(lightFunc, vec2(sa, spec.a)).r);
// add the two types of shiny together
@ -133,28 +271,30 @@ void main()
color.rgb += spec_contrib;
// add reflection map - EXPERIMENTAL WORK IN PROGRESS
float reflection_lods = 11; // TODO -- base this on resolution of reflection map instead of hard coding
float min_lod = textureQueryLod(reflectionMap,env_vec).y; // lower is higher res
//vec3 reflected_color = texture(reflectionMap, env_vec, (1.0-spec.a)*reflection_lod).rgb;
vec3 reflected_color = textureLod(reflectionMap, env_vec, max(min_lod, (1.0-spec.a)*reflection_lods)).rgb;
//vec3 reflected_color = texture(reflectionMap, env_vec).rgb;
//vec3 reflected_color = normalize(env_vec)*0.5+0.5;
reflected_color *= spec.rgb;
float lod = (1.0-spec.a)*reflection_lods;
vec3 reflected_color = sampleRefMap(pos.xyz, normalize(refnormpersp), lod);
reflected_color *= 0.5; // fudge darker, not sure where there's a multiply by two and it's late
float fresnel = 1.0+dot(normalize(pos.xyz), norm.xyz);
fresnel += spec.a;
fresnel *= fresnel;
//fresnel *= spec.a;
reflected_color *= spec.rgb*min(fresnel, 1.0);
//reflected_color = srgb_to_linear(reflected_color);
vec3 mixer = clamp(color.rgb + vec3(1,1,1) - spec.rgb, vec3(0,0,0), vec3(1,1,1));
color.rgb = mix(reflected_color*sqrt(spec.a*0.8), color, mixer);
//color.rgb = mix(reflected_color * spec.rgb * sqrt(spec.a*0.8), color.rgb, color.rgb);
//color.rgb += reflected_color * spec.rgb; // * sqrt(spec.a*0.8), color.rgb, color.rgb);
color.rgb = mix(reflected_color, color, mixer);
}
color.rgb = mix(color.rgb, diffuse.rgb, diffuse.a);
if (envIntensity > 0.0)
{ // add environmentmap
vec3 reflected_color = textureCube(environmentMap, env_vec).rgb;
color = mix(color.rgb, reflected_color, envIntensity);
vec3 reflected_color = sampleRefMap(pos.xyz, normalize(refnormpersp), 0.0);
float fresnel = 1.0+dot(normalize(pos.xyz), norm.xyz);
fresnel *= fresnel;
fresnel = fresnel * 0.95 + 0.05;
reflected_color *= fresnel;
color = mix(color.rgb, reflected_color, envIntensity);
}
if (norm.w < 0.5)
@ -172,6 +312,4 @@ void main()
// convert to linear as fullscreen lights need to sum in linear colorspace
// and will be gamma (re)corrected downstream...
frag_color.rgb = srgb_to_linear(color.rgb);
//frag_color.r = 1.0;
frag_color.a = bloom;
}

View File

@ -1512,22 +1512,23 @@ bool LLAppViewer::doFrame()
// Render scene.
// *TODO: Should we run display() even during gHeadlessClient? DK 2011-02-18
if (!LLApp::isExiting() && !gHeadlessClient && gViewerWindow)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df Display" )
pingMainloopTimeout("Main:Display");
gGLActive = TRUE;
if (!LLApp::isExiting() && !gHeadlessClient && gViewerWindow)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df Display");
pingMainloopTimeout("Main:Display");
gGLActive = TRUE;
display();
display();
{
LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df Snapshot" )
pingMainloopTimeout("Main:Snapshot");
LLFloaterSnapshot::update(); // take snapshots
LLFloaterOutfitSnapshot::update();
gGLActive = FALSE;
}
}
{
LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df Snapshot");
pingMainloopTimeout("Main:Snapshot");
gPipeline.mReflectionMapManager.update();
LLFloaterSnapshot::update(); // take snapshots
LLFloaterOutfitSnapshot::update();
gGLActive = FALSE;
}
}
}
{

View File

@ -1,116 +0,0 @@
/**
* @file llenvironmentmap.cpp
* @brief LLEnvironmentMap class implementation
*
* $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$
*/
#include "llviewerprecompiledheaders.h"
#include "llenvironmentmap.h"
#include "pipeline.h"
#include "llviewerwindow.h"
LLEnvironmentMap::LLEnvironmentMap()
{
mOrigin.setVec(0, 0, 0);
}
void LLEnvironmentMap::update(const LLVector3& origin, U32 resolution)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
mOrigin = origin;
// allocate images
std::vector<LLPointer<LLImageRaw> > rawimages;
rawimages.reserve(6);
for (int i = 0; i < 6; ++i)
{
rawimages.push_back(new LLImageRaw(resolution, resolution, 3));
}
// ============== modified copy/paste of LLFloater360Capture::capture360Images() follows ==============
// these are the 6 directions we will point the camera, see LLCubeMap::mTargets
LLVector3 look_dirs[6] = {
LLVector3(-1, 0, 0),
LLVector3(1, 0, 0),
LLVector3(0, -1, 0),
LLVector3(0, 1, 0),
LLVector3(0, 0, -1),
LLVector3(0, 0, 1)
};
LLVector3 look_upvecs[6] = {
LLVector3(0, -1, 0),
LLVector3(0, -1, 0),
LLVector3(0, 0, -1),
LLVector3(0, 0, 1),
LLVector3(0, -1, 0),
LLVector3(0, -1, 0)
};
// save current view/camera settings so we can restore them afterwards
S32 old_occlusion = LLPipeline::sUseOcclusion;
// set new parameters specific to the 360 requirements
LLPipeline::sUseOcclusion = 0;
LLViewerCamera* camera = LLViewerCamera::getInstance();
LLVector3 old_origin = camera->getOrigin();
F32 old_fov = camera->getView();
F32 old_aspect = camera->getAspect();
F32 old_yaw = camera->getYaw();
// camera constants for the square, cube map capture image
camera->setAspect(1.0); // must set aspect ratio first to avoid undesirable clamping of vertical FoV
camera->setView(F_PI_BY_TWO);
camera->yaw(0.0);
camera->setOrigin(mOrigin);
// for each of the 6 directions we shoot...
for (int i = 0; i < 6; i++)
{
// set up camera to look in each direction
camera->lookDir(look_dirs[i], look_upvecs[i]);
// call the (very) simplified snapshot code that simply deals
// with a single image, no sub-images etc. but is very fast
gViewerWindow->simpleSnapshot(rawimages[i],
resolution, resolution, 1);
}
// restore original view/camera/avatar settings settings
camera->setAspect(old_aspect);
camera->setView(old_fov);
camera->yaw(old_yaw);
camera->setOrigin(old_origin);
LLPipeline::sUseOcclusion = old_occlusion;
// ====================================================
mCubeMap = new LLCubeMap(false);
mCubeMap->initEnvironmentMap(rawimages);
}

View File

@ -0,0 +1,64 @@
/**
* @file llreflectionmap.cpp
* @brief LLReflectionMap class implementation
*
* $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$
*/
#include "llviewerprecompiledheaders.h"
#include "llreflectionmap.h"
#include "pipeline.h"
#include "llviewerwindow.h"
LLReflectionMap::LLReflectionMap()
{
}
void LLReflectionMap::update(const LLVector3& origin, U32 resolution)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
llassert(LLPipeline::sRenderDeferred);
// make sure resolution is < gPipeline.mDeferredScreen.getWidth()
while (resolution > gPipeline.mDeferredScreen.getWidth() ||
resolution > gPipeline.mDeferredScreen.getHeight())
{
resolution /= 2;
}
if (resolution == 0)
{
return;
}
mOrigin.load3(origin.mV);
mCubeMap = new LLCubeMap(false);
mCubeMap->initReflectionMap(resolution);
gViewerWindow->cubeSnapshot(origin, mCubeMap);
mCubeMap->generateMipMaps();
}

View File

@ -1,6 +1,6 @@
/**
* @file llenvironmentmap.h
* @brief LLEnvironmentMap class declaration
* @file llreflectionmap.h
* @brief LLReflectionMap class declaration
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Second Life Viewer Source Code
@ -28,21 +28,24 @@
#include "llcubemap.h"
class LLEnvironmentMap
class LLReflectionMap : public LLRefCount
{
public:
// allocate an environment map of the given resolution
LLEnvironmentMap();
LLReflectionMap();
// update this environment map
// origin - position in agent space to generate environment map from in agent space
// resolution - size of cube map to generate
void update(const LLVector3& origin, U32 resolution);
// point at which environment map was generated from (in agent space)
LLVector4a mOrigin;
// distance from viewer camera
F32 mDistance;
// cube map used to sample this environment map
LLPointer<LLCubeMap> mCubeMap;
// point at which environment map was generated from (in agent space)
LLVector3 mOrigin;
};

View File

@ -0,0 +1,106 @@
/**
* @file llreflectionmapmanager.cpp
* @brief LLReflectionMapManager class implementation
*
* $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$
*/
#include "llviewerprecompiledheaders.h"
#include "llreflectionmapmanager.h"
#include "llviewercamera.h"
LLReflectionMapManager::LLReflectionMapManager()
{
}
struct CompareReflectionMapDistance
{
};
struct CompareProbeDistance
{
bool operator()(const LLReflectionMap& lhs, const LLReflectionMap& rhs)
{
return lhs.mDistance < rhs.mDistance;
}
};
// helper class to seed octree with probes
void LLReflectionMapManager::update()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
// naively drop probes every 16m as we move the camera around for now
// later, use LLSpatialPartition to manage probes
const F32 PROBE_SPACING = 16.f;
const U32 MAX_PROBES = 8;
LLVector4a camera_pos;
camera_pos.load3(LLViewerCamera::instance().getOrigin().mV);
for (auto& probe : mProbes)
{
LLVector4a d;
d.setSub(camera_pos, probe.mOrigin);
probe.mDistance = d.getLength3().getF32();
}
if (mProbes.empty() || mProbes[0].mDistance > PROBE_SPACING)
{
addProbe(LLViewerCamera::instance().getOrigin());
}
// update distance to camera for all probes
std::sort(mProbes.begin(), mProbes.end(), CompareProbeDistance());
if (mProbes.size() > MAX_PROBES)
{
mProbes.resize(MAX_PROBES);
}
}
void LLReflectionMapManager::addProbe(const LLVector3& pos)
{
LLReflectionMap probe;
probe.update(pos, 1024);
mProbes.push_back(probe);
}
void LLReflectionMapManager::getReflectionMaps(std::vector<LLReflectionMap*>& maps)
{
// just null out for now
U32 i = 0;
for (i = 0; i < maps.size() && i < mProbes.size(); ++i)
{
maps[i] = &(mProbes[i]);
}
for (++i; i < maps.size(); ++i)
{
maps[i] = nullptr;
}
}

View File

@ -0,0 +1,51 @@
/**
* @file llreflectionmapmanager.h
* @brief LLReflectionMapManager class declaration
*
* $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$
*/
#pragma once
#include "llreflectionmap.h"
class LLReflectionMapManager
{
public:
// allocate an environment map of the given resolution
LLReflectionMapManager();
// maintain reflection probes
void update();
// drop a reflection probe at the specified position in agent space
void addProbe(const LLVector3& pos);
// Populate "maps" with the N most relevant Reflection Maps where N is no more than maps.size()
// If less than maps.size() ReflectionMaps are available, will assign trailing elements to nullptr.
// maps -- presized array of Reflection Map pointers
void getReflectionMaps(std::vector<LLReflectionMap*>& maps);
// list of active reflection maps
std::vector<LLReflectionMap> mProbes;
};

View File

@ -5170,6 +5170,7 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei
BOOL LLViewerWindow::simpleSnapshot(LLImageRaw* raw, S32 image_width, S32 image_height, const int num_render_passes)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_APP;
gDisplaySwapBuffers = FALSE;
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
@ -5266,6 +5267,152 @@ BOOL LLViewerWindow::simpleSnapshot(LLImageRaw* raw, S32 image_width, S32 image_
return true;
}
BOOL LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMap* cubemap)
{
// NOTE: implementation derived from LLFloater360Capture::capture360Images() and simpleSnapshot
LL_PROFILE_ZONE_SCOPED_CATEGORY_APP;
llassert(LLPipeline::sRenderDeferred);
U32 res = cubemap->getResolution();
llassert(res <= gPipeline.mDeferredScreen.getWidth());
llassert(res <= gPipeline.mDeferredScreen.getHeight());
// save current view/camera settings so we can restore them afterwards
S32 old_occlusion = LLPipeline::sUseOcclusion;
// set new parameters specific to the 360 requirements
LLPipeline::sUseOcclusion = 0;
LLViewerCamera* camera = LLViewerCamera::getInstance();
LLVector3 old_origin = camera->getOrigin();
F32 old_fov = camera->getView();
F32 old_aspect = camera->getAspect();
F32 old_yaw = camera->getYaw();
// camera constants for the square, cube map capture image
camera->setAspect(1.0); // must set aspect ratio first to avoid undesirable clamping of vertical FoV
camera->setView(F_PI_BY_TWO);
camera->yaw(0.0);
camera->setOrigin(origin);
gDisplaySwapBuffers = FALSE;
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
BOOL prev_draw_ui = gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI) ? TRUE : FALSE;
if (prev_draw_ui != false)
{
LLPipeline::toggleRenderDebugFeature(LLPipeline::RENDER_DEBUG_FEATURE_UI);
}
LLPipeline::sShowHUDAttachments = FALSE;
LLRect window_rect = getWorldViewRectRaw();
LLRenderTarget scratch_space; // TODO: hold onto "scratch space" render target and allocate oncer per session (allocate takes > 1ms)
U32 color_fmt = GL_RGBA;
const bool use_depth_buffer = true;
const bool use_stencil_buffer = true;
if (scratch_space.allocate(res, res, color_fmt, use_depth_buffer, use_stencil_buffer))
{
mWorldViewRectRaw.set(0, res, res, 0);
scratch_space.bindTarget();
}
else
{
return FALSE;
}
// "target" parameter of glCopyTexImage2D for each side of cubemap
U32 targets[6] = {
GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB
};
// these are the 6 directions we will point the camera, see LLCubeMap::mTargets
LLVector3 look_dirs[6] = {
LLVector3(-1, 0, 0),
LLVector3(1, 0, 0),
LLVector3(0, -1, 0),
LLVector3(0, 1, 0),
LLVector3(0, 0, -1),
LLVector3(0, 0, 1)
};
LLVector3 look_upvecs[6] = {
LLVector3(0, -1, 0),
LLVector3(0, -1, 0),
LLVector3(0, 0, -1),
LLVector3(0, 0, 1),
LLVector3(0, -1, 0),
LLVector3(0, -1, 0)
};
// for each of six sides of cubemap
for (int i = 0; i < 6; ++i)
{
// set up camera to look in each direction
camera->lookDir(look_dirs[i], look_upvecs[i]);
// turning this flag off here prohibits the screen swap
// to present the new page to the viewer - this stops
// the black flash in between captures when the number
// of render passes is more than 1. We need to also
// set it here because code in LLViewerDisplay resets
// it to TRUE each time.
gDisplaySwapBuffers = FALSE;
// actually render the scene
const U32 subfield = 0;
const bool do_rebuild = true;
const F32 zoom = 1.0;
const bool for_snapshot = TRUE;
display(do_rebuild, zoom, subfield, for_snapshot);
// copy results to cube map face
cubemap->enable(0);
cubemap->bind();
glCopyTexImage2D(targets[i], 0, GL_RGB, 0, 0, res, res, 0);
gGL.getTexUnit(0)->disable();
cubemap->disable();
}
gDisplaySwapBuffers = FALSE;
gDepthDirty = TRUE;
if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
{
if (prev_draw_ui != false)
{
LLPipeline::toggleRenderDebugFeature(LLPipeline::RENDER_DEBUG_FEATURE_UI);
}
}
LLPipeline::sShowHUDAttachments = TRUE;
gPipeline.resetDrawOrders();
mWorldViewRectRaw = window_rect;
scratch_space.flush();
scratch_space.release();
// restore original view/camera/avatar settings settings
camera->setAspect(old_aspect);
camera->setView(old_fov);
camera->yaw(old_yaw);
camera->setOrigin(old_origin);
LLPipeline::sUseOcclusion = old_occlusion;
// ====================================================
return true;
}
void LLViewerWindow::destroyWindow()
{
if (mWindow)

View File

@ -68,6 +68,7 @@ class LLWindowListener;
class LLViewerWindowListener;
class LLVOPartGroup;
class LLPopupView;
class LLCubeMap;
#define PICK_HALF_WIDTH 5
#define PICK_DIAMETER (2 * PICK_HALF_WIDTH + 1)
@ -360,6 +361,17 @@ public:
BOOL simpleSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height, const int num_render_passes);
// take a cubemap snapshot
// origin - vantage point to take the snapshot from
// cubemap - cubemap to store the results
BOOL cubeSnapshot(const LLVector3& origin, LLCubeMap* cubemap);
// special implementation of simpleSnapshot for reflection maps
BOOL reflectionSnapshot(LLImageRaw* raw, S32 image_width, S32 image_height, const int num_render_passes);
BOOL thumbnailSnapshot(LLImageRaw *raw, S32 preview_width, S32 preview_height, BOOL show_ui, BOOL show_hud, BOOL do_rebuild, LLSnapshotModel::ESnapshotLayerType type);
BOOL isSnapshotLocSet() const;
void resetSnapshotLoc() const;

View File

@ -767,42 +767,16 @@ void LLPipeline::allocatePhysicsBuffer()
bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
{
refreshCachedSettings();
bool save_settings = sRenderDeferred;
if (save_settings)
{
// Set this flag in case we crash while resizing window or allocating space for deferred rendering targets
gSavedSettings.setBOOL("RenderInitError", TRUE);
gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE );
}
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
eFBOStatus ret = doAllocateScreenBuffer(resX, resY);
if (save_settings)
{
// don't disable shaders on next session
gSavedSettings.setBOOL("RenderInitError", FALSE);
gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE );
}
if (ret == FBO_FAILURE)
{ //FAILSAFE: screen buffer allocation failed, disable deferred rendering if it's enabled
//NOTE: if the session closes successfully after this call, deferred rendering will be
// disabled on future sessions
if (LLPipeline::sRenderDeferred)
{
gSavedSettings.setBOOL("RenderDeferred", FALSE);
LLPipeline::refreshCachedSettings();
}
}
return ret == FBO_SUCCESS_FULLRES;
}
LLPipeline::eFBOStatus LLPipeline::doAllocateScreenBuffer(U32 resX, U32 resY)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
// try to allocate screen buffers at requested resolution and samples
// - on failure, shrink number of samples and try again
// - if not multisampled, shrink resolution and try again (favor X resolution over Y)
@ -856,8 +830,7 @@ LLPipeline::eFBOStatus LLPipeline::doAllocateScreenBuffer(U32 resX, U32 resY)
bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples)
{
refreshCachedSettings();
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
// remember these dimensions
mScreenWidth = resX;
mScreenHeight = resY;
@ -961,8 +934,7 @@ inline U32 BlurHappySize(U32 x, F32 scale) { return U32( x * scale + 16.0f) & ~0
bool LLPipeline::allocateShadowBuffer(U32 resX, U32 resY)
{
refreshCachedSettings();
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
if (LLPipeline::sRenderDeferred)
{
S32 shadow_detail = RenderShadowDetail;
@ -1053,6 +1025,7 @@ void LLPipeline::updateRenderDeferred()
// static
void LLPipeline::refreshCachedSettings()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
LLPipeline::sAutoMaskAlphaDeferred = gSavedSettings.getBOOL("RenderAutoMaskAlphaDeferred");
LLPipeline::sAutoMaskAlphaNonDeferred = gSavedSettings.getBOOL("RenderAutoMaskAlphaNonDeferred");
LLPipeline::sUseFarClip = gSavedSettings.getBOOL("RenderUseFarClip");
@ -8204,12 +8177,41 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_
channel = shader.enableTexture(LLShaderMgr::REFLECTION_MAP, LLTexUnit::TT_CUBE_MAP);
if (channel > -1)
{
LLCubeMap* cube_map = mEnvironmentMap.mCubeMap;
if (cube_map)
mReflectionMaps.resize(8); //TODO -- declare the number of reflection maps the shader knows about somewhere sane
mReflectionMapManager.getReflectionMaps(mReflectionMaps);
LLVector3 origin[8]; //origin of refmaps in clip space
// load modelview matrix into matrix 4a
LLMatrix4a modelview;
modelview.loadu(gGLModelView);
LLVector4a oa; // scratch space for transformed origin
S32 count = 0;
for (auto* refmap : mReflectionMaps)
{
if (refmap)
{
LLCubeMap* cubemap = refmap->mCubeMap;
if (cubemap)
{
cubemap->enable(channel + count);
cubemap->bind();
modelview.affineTransform(refmap->mOrigin, oa);
origin[count].set(oa.getF32ptr());
count++;
}
}
}
if (count > 0)
{
LLStaticHashedString refmapCount("refmapCount");
LLStaticHashedString refOrigin("refOrigin");
shader.uniform1i(refmapCount, count);
shader.uniform3fv(refOrigin, count, (F32*)origin);
setup_env_mat = true;
cube_map->enable(channel);
cube_map->bind();
}
}
@ -9125,10 +9127,14 @@ void LLPipeline::unbindDeferredShader(LLGLSLShader &shader)
channel = shader.disableTexture(LLShaderMgr::REFLECTION_MAP, LLTexUnit::TT_CUBE_MAP);
if (channel > -1)
{
LLCubeMap* cube_map = mEnvironmentMap.mCubeMap;
if (cube_map)
for (int i = 0; i < mReflectionMaps.size(); ++i)
{
cube_map->disable();
gGL.getTexUnit(channel + i)->disable();
}
if (channel == 0)
{
gGL.getTexUnit(channel)->enable(LLTexUnit::TT_TEXTURE);
}
}
@ -11498,6 +11504,7 @@ void LLPipeline::restoreHiddenObject( const LLUUID& id )
void LLPipeline::overrideEnvironmentMap()
{
mEnvironmentMap.update(LLViewerCamera::instance().getOrigin(), 1024);
mReflectionMapManager.mProbes.clear();
mReflectionMapManager.addProbe(LLViewerCamera::instance().getOrigin());
}

View File

@ -38,7 +38,7 @@
#include "llgl.h"
#include "lldrawable.h"
#include "llrendertarget.h"
#include "llenvironmentmap.h"
#include "llreflectionmapmanager.h"
#include <stack>
@ -427,7 +427,7 @@ public:
void hideObject( const LLUUID& id );
void restoreHiddenObject( const LLUUID& id );
LLEnvironmentMap mEnvironmentMap;
LLReflectionMapManager mReflectionMapManager;
void overrideEnvironmentMap();
private:
@ -661,6 +661,9 @@ public:
//utility buffer for rendering cubes, 8 vertices are corners of a cube [-1, 1]
LLPointer<LLVertexBuffer> mCubeVB;
//list of currently bound reflection maps
std::vector<LLReflectionMap*> mReflectionMaps;
//sun shadow map
LLRenderTarget mShadow[6];
LLRenderTarget mShadowOcclusion[6];