Celestia

Форк
0
/
ringrenderer.cpp 
263 строки · 8.5 Кб
1
// ringrenderer.cpp
2
//
3
// Copyright (C) 2006-2024, the Celestia Development Team
4
// Original version by Chris Laurel <claurel@gmail.com>
5
//
6
// This program is free software; you can redistribute it and/or
7
// modify it under the terms of the GNU General Public License
8
// as published by the Free Software Foundation; either version 2
9
// of the License, or (at your option) any later version.
10

11
#include "ringrenderer.h"
12

13
#include <algorithm>
14
#include <cmath>
15
#include <vector>
16

17
#include <Eigen/Core>
18

19
#include <celcompat/numbers.h>
20
#include <celengine/body.h>
21
#include <celengine/lightenv.h>
22
#include <celengine/multitexture.h>
23
#include <celengine/render.h>
24
#include <celengine/renderinfo.h>
25
#include <celengine/shadermanager.h>
26
#include <celmath/mathlib.h>
27

28
namespace celestia::render
29
{
30

31
namespace
32
{
33

34
constexpr float SegmentSizeThreshold = 30.0f;
35
constexpr std::uint32_t BaseSectionCount = UINT32_C(180);
36

37
struct RingVertex
38
{
39
    std::array<float, 3>  pos;
40
    std::array<unsigned short, 2> tex;
41
};
42

43
ShaderProperties
44
createShaderProperties(const LightingState& ls,
45
                       const Texture* ringsTex,
46
                       bool renderShadow)
47
{
48
    // Set up the shader properties for ring rendering
49
    ShaderProperties shadprop;
50
    shadprop.lightModel = LightingModel::RingIllumModel;
51
    shadprop.nLights = static_cast<std::uint16_t>(std::min(ls.nLights, MaxShaderLights));
52

53
    if (renderShadow)
54
    {
55
        // Set one shadow (the planet's) per light
56
        for (unsigned int li = 0; li < ls.nLights; li++)
57
            shadprop.setEclipseShadowCountForLight(li, 1);
58
    }
59

60
    if (ringsTex != nullptr)
61
        shadprop.texUsage = TexUsage::DiffuseTexture;
62

63
    return shadprop;
64
}
65

66
void
67
setUpShadowParameters(CelestiaGLProgram* prog,
68
                      const LightingState& ls,
69
                      float planetOblateness)
70
{
71
    for (unsigned int li = 0; li < ls.nLights; li++)
72
    {
73
        const DirectionalLight& light = ls.lights[li];
74

75
        // Compute the projection vectors based on the sun direction.
76
        // I'm being a little careless here--if the sun direction lies
77
        // along the y-axis, this will fail.  It's unlikely that a
78
        // planet would ever orbit underneath its sun (an orbital
79
        // inclination of 90 degrees), but this should be made
80
        // more robust anyway.
81
        Eigen::Vector3f axis = Eigen::Vector3f::UnitY().cross(light.direction_obj);
82
        float cosAngle = Eigen::Vector3f::UnitY().dot(light.direction_obj);
83
        axis.normalize();
84

85
        float tScale = 1.0f;
86
        if (planetOblateness != 0.0f)
87
        {
88
            // For oblate planets, the size of the shadow volume will vary
89
            // based on the light direction.
90

91
            // A vertical slice of the planet is an ellipse
92
            float a = 1.0f;                          // semimajor axis
93
            float b = a * (1.0f - planetOblateness); // semiminor axis
94
            float ecc2 = 1.0f - (b * b) / (a * a);   // square of eccentricity
95

96
            // Calculate the radius of the ellipse at the incident angle of the
97
            // light on the ring plane + 90 degrees.
98
            float r = a * std::sqrt((1.0f - ecc2) /
99
                                    (1.0f - ecc2 * math::square(cosAngle)));
100

101
            tScale *= a / r;
102
        }
103

104
        // The s axis is perpendicular to the shadow axis in the plane of the
105
        // of the rings, and the t axis completes the orthonormal basis.
106
        Eigen::Vector3f sAxis = axis * 0.5f;
107
        Eigen::Vector3f tAxis = (axis.cross(light.direction_obj)) * 0.5f * tScale;
108
        Eigen::Vector4f texGenS;
109
        texGenS.head(3) = sAxis;
110
        texGenS[3] = 0.5f;
111
        Eigen::Vector4f texGenT;
112
        texGenT.head(3) = tAxis;
113
        texGenT[3] = 0.5f;
114

115
        // r0 and r1 determine the size of the planet's shadow and penumbra
116
        // on the rings.
117
        // A more accurate ring shadow calculation would set r1 / r0
118
        // to the ratio of the apparent sizes of the planet and sun as seen
119
        // from the rings. Even more realism could be attained by letting
120
        // this ratio vary across the rings, though it may not make enough
121
        // of a visual difference to be worth the extra effort.
122
        float r0 = 0.24f;
123
        float r1 = 0.25f;
124
        float bias = 1.0f / (1.0f - r1 / r0);
125

126
        prog->shadows[li][0].texGenS = texGenS;
127
        prog->shadows[li][0].texGenT = texGenT;
128
        prog->shadows[li][0].maxDepth = 1.0f;
129
        prog->shadows[li][0].falloff = bias / r0;
130
    }
131
}
132

133
} // end unnamed namespace
134

135
RingRenderer::RingRenderer(Renderer& _renderer) : renderer(_renderer)
136
{
137
    // Initialize section scales
138
    std::uint32_t nSections = BaseSectionCount;
139
    for (unsigned int i = 0; i < RingRenderer::nLODs - 1; ++i)
140
    {
141
        sectionScales[i] = static_cast<float>(std::tan(celestia::numbers::pi / static_cast<double>(nSections)));
142
        nSections <<= 1;
143
    }
144
}
145

146
// Render a planetary ring system
147
void
148
RingRenderer::renderRings(RingSystem& rings,
149
                          const RenderInfo& ri,
150
                          const LightingState& ls,
151
                          float planetRadius,
152
                          float planetOblateness,
153
                          bool renderShadow,
154
                          float segmentSizeInPixels,
155
                          const Matrices &m,
156
                          bool inside)
157
{
158
    float inner = rings.innerRadius / planetRadius;
159
    float outer = rings.outerRadius / planetRadius;
160
    Texture* ringsTex = rings.texture.find(renderer.getResolution());
161

162
    ShaderProperties shadprop = createShaderProperties(ls, ringsTex, renderShadow);
163

164
    // Get a shader for the current rendering configuration
165
    auto* prog = renderer.getShaderManager().getShader(shadprop);
166
    if (prog == nullptr)
167
        return;
168

169
    prog->use();
170
    prog->setMVPMatrices(*m.projection, *m.modelview);
171

172
    prog->eyePosition = ls.eyePos_obj;
173
    prog->ambientColor = ri.ambientColor.toVector3();
174
    prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black);
175

176
    prog->ringRadius = inner;
177
    prog->ringWidth = outer - inner;
178

179
    setUpShadowParameters(prog, ls, planetOblateness);
180

181
    if (ringsTex != nullptr)
182
        ringsTex->bind();
183

184
    // Determine level of detail
185
    std::uint32_t nSections = BaseSectionCount;
186
    unsigned int level = 0;
187
    for (level = 0U; level < nLODs - 1U; ++level)
188
    {
189
        if (float s = segmentSizeInPixels * sectionScales[level]; s < SegmentSizeThreshold)
190
            break;
191

192
        nSections <<= 1;
193
    }
194

195
    Renderer::PipelineState ps;
196
    ps.blending = true;
197
    ps.blendFunc = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA};
198
    ps.depthTest = true;
199
    ps.depthMask = inside;
200
    renderer.setPipelineState(ps);
201

202
    renderLOD(level, nSections);
203
}
204

205
void
206
RingRenderer::initializeLOD(unsigned int level, std::uint32_t nSections)
207
{
208
    std::vector<RingVertex> ringCoord;
209
    ringCoord.reserve(2 * nSections);
210

211
    constexpr float angle = 2.0f * celestia::numbers::pi_v<float>;
212
    for (std::uint32_t i = 0; i <= nSections; i++)
213
    {
214
        float theta = angle * static_cast<float>(i) / static_cast<float>(nSections);
215
        float s, c;
216
        math::sincos(theta, s, c);
217

218
        RingVertex vertex;
219
        // inner point
220
        vertex.pos[0] = c;
221
        vertex.pos[1] = 0.0f;
222
        vertex.pos[2] = s;
223
        vertex.tex[0] = 0;
224
        vertex.tex[1] = 0;
225
        ringCoord.push_back(vertex);
226

227
        // outer point
228
        vertex.tex[0] = 1;
229

230
        ringCoord.push_back(vertex);
231
    }
232

233
    const auto& bo = buffers[level].emplace(gl::Buffer::TargetHint::Array, ringCoord);
234
    auto& vo = vertexObjects[level].emplace(gl::VertexObject::Primitive::TriangleStrip);
235
    vo.setCount(static_cast<int>((nSections + 1) * 2))
236
      .addVertexBuffer(bo,
237
                       CelestiaGLProgram::TextureCoord0AttributeIndex,
238
                       2,
239
                       gl::VertexObject::DataType::UnsignedShort,
240
                       false,
241
                       sizeof(RingVertex),
242
                       offsetof(RingVertex, tex))
243
      .addVertexBuffer(bo,
244
                       CelestiaGLProgram::VertexCoordAttributeIndex,
245
                       3,
246
                       gl::VertexObject::DataType::Float,
247
                       false,
248
                       sizeof(RingVertex),
249
                       offsetof(RingVertex, pos));
250
    bo.unbind();
251
}
252

253
void
254
RingRenderer::renderLOD(unsigned int level, std::uint32_t nSections)
255
{
256
    if (!vertexObjects[level].has_value())
257
        initializeLOD(level, nSections);
258
    glDisable(GL_CULL_FACE);
259
    vertexObjects[level]->draw();
260
    glEnable(GL_CULL_FACE);
261
}
262

263
} // end namespace celestia::render
264

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.