@itwin/core-frontend
Version:
iTwin.js frontend components
264 lines (260 loc) • 12.5 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module WebGL
*/
import { Angle, Point3d, Vector3d } from "@itwin/core-geometry";
import { Npc } from "@itwin/core-common";
import { AttributeMap } from "../AttributeMap";
import { fromSumOf } from "../FrustumUniforms";
import { TextureUnit } from "../RenderFlags";
import { System } from "../System";
import { assignFragColor } from "./Fragment";
import { createViewportQuadBuilder } from "./ViewportQuad";
import { addAtmosphericScatteringEffect } from "./Atmosphere";
const computeGradientValue = `
// For the gradient sky it's good enough to calculate these in the vertex shader.
vec3 eyeToVert = a_worldPos - u_worldEye;
float radius = sqrt(eyeToVert.x * eyeToVert.x + eyeToVert.y * eyeToVert.y);
float zValue = eyeToVert.z - radius * u_zOffset;
float d = atan(zValue, radius);
if (u_skyParams.x < 0.0) { // 2-color gradient
d = 0.5 - d / 3.14159265359;
return vec4(d, 0.0, 0.0, 0.0);
}
d = d / 1.570796326795;
return vec4(d, 1.0 - (d - horizonSize) / (1.0 - horizonSize), 1.0 - (-d - horizonSize) / (1.0 - horizonSize), (d + horizonSize) / (horizonSize * 2.0));
`;
const computeSkySphereColorGradient = `
if (u_skyParams.x < 0.0) // 2-color
return vec4(mix(u_zenithColor, u_nadirColor, v_gradientValue.x), 1.0);
if (v_gradientValue.x > horizonSize) // above horizon
return vec4(mix(u_zenithColor, u_skyColor, pow(v_gradientValue.y, u_skyParams.y)), 1.0);
else if (v_gradientValue.x < -horizonSize) // below horizon
return vec4(mix(u_nadirColor, u_groundColor, pow(v_gradientValue.z, u_skyParams.z)), 1.0);
return vec4(mix(u_groundColor, u_skyColor, v_gradientValue.w), 1.0);
`;
const computeSkySphereColorAtmosphere = `
return vec4(0.0, 0.0, 0.0, 1.0);
`;
const computeEyeToVert = "v_eyeToVert = a_worldPos - u_worldEye;";
const computeSkySphereColorTexture = `
// For the texture we must calculate these per pixel. Alternatively we could use a finer mesh.
float radius = sqrt(v_eyeToVert.x * v_eyeToVert.x + v_eyeToVert.y * v_eyeToVert.y);
float zValue = v_eyeToVert.z - radius * u_zOffset;
float u = 0.25 - (atan(v_eyeToVert.y, v_eyeToVert.x) + u_rotation) / 6.28318530718;
float v = 0.5 - atan(zValue, radius) / 3.14159265359;
if (u < 0.0)
u += 1.0;
if (v < 0.0)
v += 1.0;
return TEXTURE(s_skyTxtr, vec2(u, v));
`;
/**
* Eye Space for the SkySphere is unique because the ViewportQuad is already aligned with the view.
* For this reason, the modelView matrix is not useful in calculating eyeSpace.
* Instead, we can calculate the eyeSpace coordinates via the frustum values directly.
*/
const computeEyeSpace = `
vec3 computeEyeSpace(vec4 rawPos) {
vec3 pos01 = rawPos.xyz * 0.5 + 0.5;
float top = u_frustumPlanes.x;
float bottom = u_frustumPlanes.y;
float left = u_frustumPlanes.z;
float right = u_frustumPlanes.w;
return vec3(
mix(left, right, pos01.x),
mix(bottom, top, pos01.y),
-u_frustum.x
);
}`;
const scratch3Floats = new Float32Array(3);
const scratchVec3 = new Vector3d();
const scratchPoint3 = new Point3d();
/** @internal */
function modulateColor(colorIn, t, colorOut) {
const b = 1.0 - t;
colorOut[0] = colorIn[0] * b;
colorOut[1] = colorIn[1] * b;
colorOut[2] = colorIn[2] * b;
}
/** @internal */
function addGradientUniforms(builder) {
builder.vert.addUniform("u_skyParams", 4 /* VariableType.Vec3 */, (shader) => {
shader.addGraphicUniform("u_skyParams", (uniform, params) => {
const geom = params.geometry;
uniform.setUniform3fv(geom.typeAndExponents);
});
});
builder.vert.addUniform("u_zOffset", 2 /* VariableType.Float */, (shader) => {
shader.addGraphicUniform("u_zOffset", (uniform, params) => {
const geom = params.geometry;
uniform.setUniform1f(geom.zOffset);
});
});
builder.frag.addUniform("u_skyParams", 4 /* VariableType.Vec3 */, (shader) => {
shader.addGraphicUniform("u_skyParams", (uniform, params) => {
const geom = params.geometry;
uniform.setUniform3fv(geom.typeAndExponents);
});
});
builder.frag.addUniform("u_zenithColor", 4 /* VariableType.Vec3 */, (shader) => {
shader.addGraphicUniform("u_zenithColor", (uniform, params) => {
const geom = params.geometry;
const plan = params.target.plan;
if (plan.backgroundMapOn && plan.isGlobeMode3D) {
modulateColor(geom.zenithColor, plan.globalViewTransition, scratch3Floats);
uniform.setUniform3fv(scratch3Floats);
}
else
uniform.setUniform3fv(geom.zenithColor);
});
});
builder.frag.addUniform("u_skyColor", 4 /* VariableType.Vec3 */, (shader) => {
shader.addGraphicUniform("u_skyColor", (uniform, params) => {
const geom = params.geometry;
const plan = params.target.plan;
if (plan.backgroundMapOn && plan.isGlobeMode3D) {
modulateColor(geom.skyColor, plan.globalViewTransition, scratch3Floats);
uniform.setUniform3fv(scratch3Floats);
}
else
uniform.setUniform3fv(geom.skyColor);
});
});
builder.frag.addUniform("u_groundColor", 4 /* VariableType.Vec3 */, (shader) => {
shader.addGraphicUniform("u_groundColor", (uniform, params) => {
const geom = params.geometry;
const plan = params.target.plan;
if (plan.backgroundMapOn) {
let clr = geom.skyColor;
if (-1 === geom.typeAndExponents[0]) // 2-color gradient
clr = geom.zenithColor;
if (plan.isGlobeMode3D) {
modulateColor(clr, plan.globalViewTransition, scratch3Floats);
uniform.setUniform3fv(scratch3Floats);
}
else
uniform.setUniform3fv(clr);
}
else {
uniform.setUniform3fv(geom.groundColor);
}
});
});
builder.frag.addUniform("u_nadirColor", 4 /* VariableType.Vec3 */, (shader) => {
shader.addGraphicUniform("u_nadirColor", (uniform, params) => {
const geom = params.geometry;
const plan = params.target.plan;
if (plan.backgroundMapOn) {
let clr = geom.skyColor;
if (-1 === geom.typeAndExponents[0]) // 2-color gradient
clr = geom.nadirColor;
if (plan.isGlobeMode3D) {
modulateColor(clr, plan.globalViewTransition, scratch3Floats);
uniform.setUniform3fv(scratch3Floats);
}
else
uniform.setUniform3fv(clr);
}
else {
uniform.setUniform3fv(geom.nadirColor);
}
});
});
}
/** @internal */
function addTextureUniforms(builder) {
builder.frag.addUniform("s_skyTxtr", 8 /* VariableType.Sampler2D */, (shader) => {
shader.addGraphicUniform("s_skyTxtr", (uniform, params) => {
const geom = params.geometry;
if (undefined !== geom.skyTexture)
geom.skyTexture.texture.bindSampler(uniform, TextureUnit.Zero);
else
System.instance.ensureSamplerBound(uniform, TextureUnit.FeatureSymbology);
});
});
builder.frag.addUniform("u_zOffset", 2 /* VariableType.Float */, (shader) => {
shader.addGraphicUniform("u_zOffset", (uniform, params) => {
const geom = params.geometry;
uniform.setUniform1f(geom.zOffset);
});
});
builder.frag.addUniform("u_rotation", 2 /* VariableType.Float */, (shader) => {
shader.addGraphicUniform("u_rotation", (uniform, params) => {
const geom = params.geometry;
uniform.setUniform1f(geom.rotation);
});
});
}
/** @internal */
export function createSkySphereBuilder(isGradient, flags) {
const attrMap = AttributeMap.findAttributeMap(isGradient ? 24 /* TechniqueId.SkySphereGradient */ : 25 /* TechniqueId.SkySphereTexture */, false);
const builder = createViewportQuadBuilder(false, attrMap);
const vert = builder.vert;
const frag = builder.frag;
vert.addFunction(computeEyeSpace);
builder.addInlineComputedVarying("v_eyeSpace", 4 /* VariableType.Vec3 */, "v_eyeSpace = computeEyeSpace(rawPosition);");
vert.addUniform("u_frustumPlanes", 5 /* VariableType.Vec4 */, (prg) => {
prg.addGraphicUniform("u_frustumPlanes", (uniform, params) => {
uniform.setUniform4fv(params.target.uniforms.frustum.planes); // { top, bottom, left, right }
});
});
vert.addUniform("u_frustum", 4 /* VariableType.Vec3 */, (prg) => {
prg.addGraphicUniform("u_frustum", (uniform, params) => {
uniform.setUniform3fv(params.target.uniforms.frustum.frustum); // { near, far, type }
});
});
frag.set(18 /* FragmentShaderComponent.AssignFragData */, assignFragColor);
if (flags.enableAtmosphere) {
frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeSkySphereColorAtmosphere);
addAtmosphericScatteringEffect(builder, true, true);
return builder;
}
builder.vert.addUniform("u_worldEye", 4 /* VariableType.Vec3 */, (shader) => {
shader.addGraphicUniform("u_worldEye", (uniform, params) => {
const frustum = params.target.planFrustum;
if (2 /* FrustumUniformType.Perspective */ === params.target.uniforms.frustum.type) {
// compute eye point from frustum.
const farLowerLeft = frustum.getCorner(Npc.LeftBottomRear);
const nearLowerLeft = frustum.getCorner(Npc.LeftBottomFront);
const scale = 1.0 / (1.0 - params.target.planFraction);
const zVec = Vector3d.createStartEnd(farLowerLeft, nearLowerLeft, scratchVec3);
const cameraPosition = fromSumOf(farLowerLeft, zVec, scale, scratchPoint3);
scratch3Floats[0] = cameraPosition.x;
scratch3Floats[1] = cameraPosition.y;
scratch3Floats[2] = cameraPosition.z;
uniform.setUniform3fv(scratch3Floats);
}
else {
const delta = Vector3d.createStartEnd(frustum.getCorner(Npc.LeftBottomRear), frustum.getCorner(Npc.LeftBottomFront), scratchVec3);
const pseudoCameraHalfAngle = 22.5;
const diagonal = frustum.getCorner(Npc.LeftBottomRear).distance(frustum.getCorner(Npc.RightTopRear));
const focalLength = diagonal / (2 * Math.atan(pseudoCameraHalfAngle * Angle.radiansPerDegree));
let zScale = focalLength / delta.magnitude();
if (zScale < 1.000001)
zScale = 1.000001; // prevent worldEye front being on or inside the frustum front plane
const worldEye = Point3d.createAdd3Scaled(frustum.getCorner(Npc.LeftBottomRear), .5, frustum.getCorner(Npc.RightTopRear), .5, delta, zScale, scratchPoint3);
scratch3Floats[0] = worldEye.x;
scratch3Floats[1] = worldEye.y;
scratch3Floats[2] = worldEye.z;
uniform.setUniform3fv(scratch3Floats);
}
});
});
if (isGradient) {
addGradientUniforms(builder);
builder.addGlobal("horizonSize", 2 /* VariableType.Float */, 3 /* ShaderType.Both */, "0.0015", true);
builder.addFunctionComputedVarying("v_gradientValue", 5 /* VariableType.Vec4 */, "computeGradientValue", computeGradientValue);
frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeSkySphereColorGradient);
}
else {
addTextureUniforms(builder);
builder.addInlineComputedVarying("v_eyeToVert", 4 /* VariableType.Vec3 */, computeEyeToVert);
frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeSkySphereColorTexture);
}
return builder;
}
//# sourceMappingURL=SkySphere.js.map