A reserved fragment shader can process lighting and other effects for fragments output from the previous shader.
As explained in 5. Shader Programs, the reserved fragment shader does not need to be loaded from a binary. To use it, attach it to the same program object as the vertex shader and geometry shader, using the special name GL_DMP_FRAGMENT_SHADER_DMP
. The reserved fragment shader is a collection of fragment processing features. These features include the following.
- Fragment lighting
- Shadows
- Fog
- Gas
- Miscellaneous (alpha test, w buffer)
There are reserved uniforms for each fragment process. These reserved uniforms have default values and must be set, as necessary, by the application.
12.1. Fragment Operation Mode
Reserved fragment processing replaces the standard OpenGL fragment pipeline (from the alpha test onward) with independent processing that can handle the special rendering passes required by shadows and gas. To switch this fragment operation mode, set the reserved uniform (dmp_FragOperation.mode)
to the desired mode using the glUniform1i
function.
Fragment Operation Mode |
Pipeline |
---|---|
|
Standard OpenGL fragment pipeline (standard mode). |
|
Fragment pipeline for the shadow accumulation pass (shadow mode). |
|
Fragment pipeline for rendering density information (gas mode). |
12.2. Fragment Lighting
The 3DS system uses fragment lighting, which calculates the primary and secondary colors for each fragment, rather than for each vertex. It also has the following features: bump mapping, which references a texture to perturb normal vectors; shadows, which involve the creation of shadow textures and color calculations; and attenuation, which is calculated from the distance to a spotlight or some other light.
The primary and secondary colors are determined by first combining multiple functions that output lookup table values based on the dot product of two vectors, and then using bitwise AND/OR operations to combine those output values. With 3DS, although you cannot fully customize the method that combines the lookup tables and vectors for the dot products, you can select different configurations from preset settings.
Nintendo expects the CTR system to use eye coordinates for several vectors, because lighting equations are considered to be in eye coordinates. Although eye coordinates are not necessarily required, all the vectors that are used must be in the same coordinate system.
To use fragment lighting, you must set dmp_FragmentLighting.enabled
to GL_TRUE
with the glUniform1i
function and enable at least one light. In the vertex shader, the normal vector, view vector, and tangent vector (when required for lighting) must be converted into a quaternion and output as a single vertex attribute.
Reserved Uniform |
Type |
Setting Value |
---|---|---|
|
|
Specify
|
12.2.1. Quaternion Conversion
Lighting calculations require all vectors to use the same coordinate system. In other words, bump mapping (explained later) must convert the perturbation normals referenced in a texture from surface-local coordinates (with the vertex at the origin and the normal vector along the positive z-axis) into eye coordinates.
The following 3x3 rotation matrix converts surface-local coordinates into eye coordinates. It comprises a normal, tangent, and binormal vector, and can be converted into a quaternion (Qx, Qy, Qz, Qw).
(E represents the eye coordinates, T is the tangent, N is the normal, B is the binormal, and S represents the surface-local coordinates.)
Fragment lighting is implemented to generate a quaternion for each fragment from a quaternion for each vertex, rather than generate a vector for each fragment from the normal, tangent, and binormal (which can be calculated from the normal and tangent) vectors input for each vertex. The quaternion is converted into the original rotation matrix during fragment light processing. To use fragment lighting, you must convert each vector into a valid quaternion in the vertex shader.
Generating Normal-Only Quaternions
The CTR-SDK sample demos include a vertex shader (l_position_view_quaternion
) that generates quaternions using only the vertex normal information. To generate a quaternion that transforms normals in surface-local coordinates to normals in perspective coordinates, the vertex shader uses the half angle vector between the unit normal vector (0, 0, 1) and a normal vector that has been transformed to perspective coordinates (Nx, Ny, Nz) as the axis of rotation, and derives a quaternion that performs 180° rotations.
For an axis of rotation (α, β, γ) and an angle of rotation θ, the derived quaternion Q would be computed as follows.
In the vertex shader, the real component of the quaternion is set to the w component.
For a θ value of 180°, this becomes:
Q = ( 0; α, β, γ )
Taking the half-angle vector as the axis of rotation, this becomes:
The orientation of the half-angle vector is undefined only when (Nx, Ny, Nz) is (0, 0, –1). In this case, assume that (α, β, γ) = (1, 0, 0).
12.2.2. Lighting Overview
3DS fragment lighting always calculates the primary and secondary colors.
The primary color is calculated first by accumulating each light's effect (with shadows, spotlight attenuation, and distance attenuation applied) on a fragment's ambient and diffuse light. The fragment's emissive light and the effect of the scene's ambient light is then added to this accumulated value. This becomes the fragment's base color.
The secondary color is calculated by accumulating each light's effect on a fragment's second specular light with shadows, spotlight attenuation, and distance attenuation applied. This color is mainly used for fragment highlights.
Like OpenGL, an object's color is determined from its ambient, diffuse, emissive, and specular light. Unlike OpenGL, however, lighting uses per-fragment calculations and second specular light, making it possible to calculate the specular light in a variety of ways. The second specular light in particular can be used to represent materials with colors that change depending on the angle.
12.2.3. Scene Settings
Fragment lighting can handle scene sizes from -216 through 215. Do not allow the distance between the viewpoint and any fragment or light in the scene to be greater than or equal to 216.
The scene affects fragments through its ambient light (the global ambient light). To specify the scene's global ambient light, set the reserved uniform dmp_FragmentLighting.ambient
to an RGBA color using the glUniform4fv
function.
Reserved Uniform |
Type |
Setting Value |
---|---|---|
|
|
Specifies the scene's global ambient light (R, G, B, A). Each component has a value between 0.0 and 1.0. This is (0.2, 0.2, 0.2, 1.0) by default. |
12.2.4. Material Settings
Material settings can be described simply as settings that use color information, such as the ambient and specular light, to represent a fragment's materials and texture. Specify material-related settings in the reserved uniforms dmp_FragmentMaterial.*
.
12.2.7. Equations for the Primary Color and 12.2.8. Equations for the Secondary Color explain how the settings are used in lighting calculations.
Reserved Uniform |
Type |
Setting Value |
---|---|---|
|
|
Specifies the ambient light (R, G, B, A). Each component has a value between 0.0 and 1.0. This is (0.2, 0.2, 0.2, 1.0) by default. |
|
|
Specifies the ambient light (R, G, B, A). Each component has a value between 0.0 and 1.0. This is (0.8, 0.8, 0.8, 1.0) by default. |
|
|
Specifies the ambient light (R, G, B, A). Each component has a value between 0.0 and 1.0. This is (0.0, 0.0, 0.0, 1.0) by default. |
|
|
Specifies specular light 0 (R, G, B, A). Each component has a value between 0.0 and 1.0. This is (0.0, 0.0, 0.0, 1.0) by default. |
|
|
Specifies specular light 1 (R, G, B, A). Each component has a value between 0.0 and 1.0. This is (0.0, 0.0, 0.0, 1.0) by default. |
|
|
Specifies the lookup tables to use for lighting calculations. This is a number from 0 through 31 for each factor. |
12.2.5. Light Settings
There are two types of light settings. One configures the effect of light on a material and the other configures the light itself. Fragment lighting can handle eight lights. Specify light-related settings in the reserved uniforms dmp_FragmentLightSource[i].*
(where i
is the light number).
12.2.7. Equations for the Primary Color and 12.2.8. Equations for the Secondary Color explain how the settings are used in lighting calculations.
Reserved Uniform |
Type |
Setting Value |
---|---|---|
|
|
Enables or disables a light. Specify |
|
|
Specifies an ambient light (R, G, B, A). Each component has a value between 0.0 and 1.0. This is (0.0, 0.0, 0.0, 0.0) by default. |
|
|
Specifies a diffuse light (R, G, B, A). Each component has a value between 0.0 and 1.0. By default, only light number 0 is (1.0, 1.0, 1.0, 1.0). |
|
|
Specifies specular light 0 (R, G, B, A). Each component has a value between 0.0 and 1.0. By default, only light number 0 is (1.0, 1.0, 1.0, 1.0). |
|
|
Specifies specular light 1 (R, G, B, A). Each component has a value between 0.0 and 1.0. This is (0.0, 0.0, 0.0, 0.0) by default. |
|
|
Specifies the position of a light source (x, y, z, w). The vector does not need to be normalized. The w component is used to distinguish between directional (0.0) and positional light sources. This is (0.0, 0.0, 1.0, 0.0) by default. |
|
|
Specifies a spotlight direction (x, y, z). The vector does not need to be normalized. (0.0, 0.0, -1.0) by default. |
|
|
Specifies whether a light is affected by shadows. Specify |
|
|
Specifies whether to use geometry factor 0 in lighting calculations. Specify |
|
|
Specifies whether to use geometry factor 1 in lighting calculations. Specify |
|
|
Specifies whether to use two-sided lighting. Specify |
|
|
Specifies whether to attenuate a spotlight based on its light distribution. Specify |
|
|
Specifies whether to attenuate a light over distance. Specify |
|
|
Specifies the distance attenuation bias. 0.0 by default. |
|
|
Specifies the distance attenuation scale. 1.0 by default. |
|
|
Specifies the lookup table to use for calculating spotlight attenuation and distance attenuation. This is a number between 0 and 31 for each factor. |
The table uses asterisks (*) to indicate dmp_FragmentLightSource[i]
. i
specifies the light number (0 to 7).
12.2.6. Lighting Environment
The reserved uniforms dmp_LightEnv.*
configure settings related to general lighting, including shadow texture selection, bump map settings, and lookup table input.
12.2.7. Equations for the Primary Color and 12.2.8. Equations for the Secondary Color explain how the settings are used in lighting calculations.
Reserved Uniform |
Type |
Setting Value |
---|---|---|
|
|
Specifies whether to convert lookup table input for each factor into absolute values. Specify |
|
|
Specifies the cosine of the angle between two vectors to use as lookup table input for each factor.
|
|
|
Specifies the scale to apply to each factor's lookup table output. After applying a scale value to the value output by the lookup table, clamping is performed within a range from 0.25 |
|
|
Specifies the texture unit to use for shadows.
|
|
|
Specifies the texture unit to use for bump maps.
|
|
|
Specifies the perturbation mode for normal and tangent vectors.
|
|
|
Specifies whether to regenerate the third component of the normal vector. Specify |
|
|
Specifies the configuration for each factor.
|
|
|
Specifies whether to invert the shadow term ( Specify |
|
|
Specifies whether to apply shadows to the primary color. Specify |
|
|
Specifies whether to apply shadows to the secondary color. Specify |
|
|
Specifies whether to apply shadows to the alpha component. Specify |
|
|
Specifies the output mode for the Fresnel factor.
|
|
|
Specifies whether to clamp the specular output value. Specify |
|
|
Specifies whether to apply output values from the lookup table for distribution 0 (D0). Specify |
|
|
Specifies whether to apply output values from the lookup table for distribution 1 (D1). Specify |
|
|
Specifies whether to apply output values from the lookup tables for reflection (RR, RG, RB). Specify |
The table uses asterisks (*) to indicate dmp_LightEnv
.
12.2.7. Equations for the Primary Color
The following formula summarizes how the primary color is calculated.
Colorprimary = ∑((Diffuse × DPLN × Shadow + Ambient) × Spot × DistAtt + Ambientglobal + Emission
The following paragraphs provide more information about how each term is calculated.
The product of the material and light color components is applied to the diffuse and ambient light.
Diffuse = Diffusematerial × Diffuselight
Ambient = Ambientmaterial × Ambientlight
The diffuse light is affected by shadows and the angle of incident light.
DPLN = max { 0, L∙N } or abs ( L∙N )
The effect of the angle of incident light is given by DPLN in the equation. This is the dot product of a normalized light vector and normal vector. For one-sided lighting this is the larger of 0
and the dot product, and for two-sided lighting this is the absolute value of the dot product. To enable one-sided or two-sided lighting, set dmp_FragmentLightSource[i].twoSideDiffuse
to GL_FALSE
or GL_TRUE
respectively.
The effect of shadow attenuation is represented by Shadow in the equation. If either dmp_LightEnv.shadowPrimary
or dmp_FragmentLightSource[i].shadowed
is GL_FALSE
, shadows have no effect and a value of 1.0
is applied. If either reserved uniform is GL_TRUE
, the value used is sampled from the texture unit specified by dmp_LightEnv.shadowSelector
. If dmp_LightEnv.invertShadow
is GL_TRUE
, the value used is actually the sampled value subtracted from 1.0
. If any texture other than a shadow texture is bound to the specified texture unit, color components are applied unchanged to the sampled value.
The effect of spotlights is represented by Spot in the equation. You can set a lookup table for each light and configure whether it is used. Use dmp_FragmentLightSource[i].spotEnabled
to configure whether spotlights are used and dmp_FragmentLightSource[i].samplerSP
to set the lookup tables used by spotlights. Use dmp_FragmentLightSource[i].spotDirection
to set the spotlight direction vector. A value of GL_LIGHT_ENV_SP_DMP
is usually specified as the lookup table input for spotlights (dmp_LightEnv.lutInputSP
).
The effect of distance attenuation is represented by DistAtt in the equation. Directional light sources are unaffected by distance attenuation.
The reserved uniform dmp_FragmentLightSource[i].distanceAttenuationEnabled
configures whether distance attenuation is applied. This can be controlled for each light. However, the effect of distance attenuation is disabled (1.
0 is applied) when dmp_LightEnv.config
is GL_LIGHT_ENV_LAYER_CONFIG7_DMP
.
The lookup table to use is set by dmp_FragmentLightSource[i].samplerDA
. Lookup table input is affected by the values set for the scale (dmp_FragmentLightSource[i].distanecAttenuationScale
) and bias (dmp_FragmentLightSource[i].distanceAttenuationBias
).
This section has so far described the effect of each light on a fragment's primary color. This effect is calculated only for valid lights and then it is added to the material's emissive color and the global ambient color, which are unaffected by lights, to compute the fragment's final primary color.
The global ambient light is the product of the material's ambient light and the scene's ambient light.
Ambientglobal = Ambientmaterial × Ambientscene
When light source 0 is disabled (dmp_FragmentLightSource[0].enabled
is GL_FALSE
), however, a value of 0.0
is applied to the global ambient light and the material's emissive light.
12.2.8. Equations for the Secondary Color
The following formula summarizes how the secondary color is calculated.
Colorsecondary = ∑((Specular0 + Specular1) × f × Shadow × Spot × DistAtt)
The following paragraphs provide more information about how each term is calculated.
Apart from the fact that dmp_LightEnv.shadowSecondary
settings are used instead of dmp_LightEnv.shadowPrimary
settings, Shadow, Spot, and DistAtt are each calculated in the same way that they are calculated for the primary color.
Specular0 and Specular1 are each calculated as follows.
Specular0 = Specular0material × Specular0light × Distribution0 × Geometry0
Specular1 = ReflectionRGB × Specular1light × Distribution1 × Geometry1
The specular term is usually calculated as the product of the specular property of a material, the specular color of a light, a distribution function, and a geometry factor. Some settings allow output from lookup tables that define different reflections for each color to be applied to the term that corresponds to a material's specular light 1. By adjusting the reflection and distribution lookup tables, you can represent fragments with a variety of different textures.
Distribution functions (factors) are represented by Distribution0 and Distribution1 in the equation. The distribution functions are configured through lookup tables for distribution 0 (D0) and distribution 1 (D1).
The reserved uniform dmp_LightEnv.lutEnabledD0
(lutEnabledD1
) controls whether these functions are used. The reserved uniform dmp_FragmentLightSource[i].samplerD0
(samplerD1
) specifies the lookup table number to use. The reserved uniforms dmp_LightEnv.lutInputD0
and dmp_LightEnv.lutInputD1
specify the lookup table input. dmp_LightEnv.absLutInputD0
and dmp_LightEnv.absLutInputD1
specify the absolute value of the input. Lookup table output accounts for the scale values specified by dmp_LightEnv.lutScaleD0
and dmp_LightEnv.lutScaleD1
.
Reflections are represented by ReflectionRGB in the equation. They use lookup tables (RR, RG, RB) to set functions that calculate the reflection for each RGB component instead of a material's specular light 1. The reserved uniform dmp_LightEnv.lutEnabledRefl
controls whether this feature is used. If GL_FALSE
is specified, the material's specular color 1 is applied. The reserved uniform dmp_FragmentLightSource[i].samplerRR
(samplerRG
, samplerRB
) specifies the lookup table number to use. The reserved uniforms dmp_LightEnv.lutInputXX
specify the lookup table input and dmp_LightEnv.absLutInputXX
specify the absolute value of the input (where XX
is RR
, RG
, or RB
).
Geometry factors are represented by Geometry0 and Geometry1 in the equation. They are used by the Cook-Torrance lighting model. The reserved uniforms dmp_FragmentLightSource[i].geomFactor0
and dmp_FragmentLightSource[i].geomFactor1
control whether they are used. A value of 1.0
is applied for a setting of GL_FALSE
and an approximation of the geometry factors used in the Cook-Torrance lighting model is applied for a setting of GL_TRUE
.
f is a function that uses the dot product of the normalized light vector and normal vector to determine whether lighting is enabled for a fragment. A value of 1.0
is always applied when dmp_LightEnv.clampHighlights
is set to GL_FALSE
. This is used to represent translucent objects that allow light to pass through them to areas where it would not otherwise reach. If GL_TRUE
is specified, a value of 0.0
is applied when the dot product is 0.0
or less and a value of 1.0
is applied when the dot product is greater than 0.0
. In this case, unlit areas have no specular light.
A fragment's final secondary color is calculated from the effect of each valid light's effect on it.
12.2.9. Alpha Component Lighting
The previous sections have explained how to calculate the primary and secondary colors, with the alpha component fixed at 1.0. Fragment lighting allows you to apply Fresnel factors and shadows to the alpha component.
Fresnel factors were originally intended to be used as lookup tables (FR) for Fresnel reflections in translucent objects, but by replacing the alpha component with lookup table output, they can also be used for other purposes.
The reserved uniform dmp_FragmentLightSource[i].samplerFR
specifies the number of the lookup table to use for the Fresnel factors. The reserved uniform dmp_LightEnv.lutInputFR
specifies the lookup table input and dmp_LightEnv.absLutInputFR
specifies the absolute values of the input. If multiple lights have been enabled, the light with the largest number has its light vector used in the dot product that is input to the lookup table. The reserved uniform dmp_LightEnv.fresnelSelector
uses the following values to control the extent to which Fresnel factors are applied.
Value |
Applies To |
---|---|
|
Nothing.(Fx the alpha component at |
|
Only the alpha component for the primary color. |
|
Only the alpha component for the secondary color. |
|
The alpha component for the primary and secondary colors. |
The Fresnel factor is also applied to the alpha component for shadows. The shadow alpha component is multiplied with the applied alpha component when a value of GL_TRUE
is specified for dmp_LightEnv.shadowAlpha
, which controls the effect on the shadow alpha component. If dmp_LightEnv.invertShadow
is GL_TRUE
, the shadow alpha value is subtracted from 1.0 (as it is for colors) before being multiplied.
12.2.10. Creating and Specifying Lookup Tables
Lighting equations use the following eight types of lookup tables.
- Reflections (three types: RR, RG, and RB)
- Distribution factors (two types: D0 and D1)
- Fresnel factors (FR)
- Spotlights (SP)
- Distance attenuation of light
The lookup tables for reflections (RR, RG, RB), distribution factors (D0, D1), and Fresnel factors are all material settings, and are common to all lights. The lookup tables for spotlights (SP) and the distance attenuation of light (DA) can be set differently for each light.
The layer configuration (dmp_LightEnv.config
) can control which lookup tables are used for each term in the secondary color's lighting equation, except for the distance attenuation of light.
Layer Configuration |
Rr |
Rg |
Rb |
D0 |
D1 |
Fr |
Sp |
Cycles |
---|---|---|---|---|---|---|---|---|
|
RR |
RR |
RR |
D0 |
- |
- |
SP |
1 |
|
RR |
RR |
RR |
- |
- |
FR |
SP |
1 |
|
RR |
RR |
RR |
D0 |
D1 |
- |
- |
1 |
|
- |
- |
- |
D0 |
D1 |
FR |
- |
1 |
|
RR |
RG |
RB |
D0 |
D1 |
- |
SP |
2 |
|
RR |
RG |
RB |
D0 |
- |
FR |
SP |
2 |
|
RR |
RR |
RR |
D0 |
D1 |
FR |
SP |
2 |
|
RR |
RG |
RB |
D0 |
D1 |
FR |
SP |
4 |
The table shows which lookup tables are used to get values for a reflection's RGB components, the distribution factors, the Fresnel factors, and the spotlight term. A value of 1.0
is applied to the lighting equation for any cell that contains a hyphen (-). In other words, the corresponding term disappears from the equation. The Cycles column shows the number of hardware cycles required for lighting calculations. To speed up lighting calculations, choose a layer configuration that minimizes this number.
When only write access to the color buffer has been configured (when the glColorMask
function has set a value of GL_TRUE
for all color buffer components and the glDisable
function has disabled GL_BLEND
and GL_COLOR_LOGIC_OP
), layer configurations from GL_LIGHT_ENV_LAYER_CONFIG4_DMP
through GL_LIGHT_ENV_LAYER_CONFIG6_DMP
require three rather than two cycles to process a single pixel.
A setting of GL_LIGHT_ENV_LAYER_CONFIG7_DMP
disables the effect of distance attenuation when the primary and secondary color are calculated.
For example, when GL_LIGHT_ENV_LAYER_CONFIG0_DMP
is set, all of the reflection RGB components are obtained from the RR lookup table, the distribution 0 values are obtained from the D0 lookup table, and the spotlight values are obtained from the SP lookup table. A fixed value of 1.0
is applied for distribution 1 and the Fresnel factor.
As explained in 7.7. Loading Lookup Tables, lookup tables are prepared by the glTexImage1D
function. The lookup tables used for fragment lighting have a fixed width
of 512 elements. The first 256 elements store the lookup table's sampling values and the last 256 elements store the differences between each of the sampling values.
The order that sampling values are stored in depends on whether lookup table input is between 0.0
and 1.0
or between –1.0
and 1.0
. The reserved uniforms dmp_LightEnv.absLutInputXX
(where XX
is D0
, D1
, RR
, RG
, RB
, FR
, or SP
) set the range of input values from 0.0
to 1.0
when GL_TRUE
is specified or from –1.
0 to 1.0
when GL_FALSE
is specified.
Procedures for getting sampling values are explained next, followed by the corresponding procedures for storing them.
If the range of input values is between 0.0
and 1.0
, each input value is multiplied by 256 and then clamped to 255. The integer portion of this number is the index for getting values. The index is first used to get a sampling value from the lookup table, and then it is incremented by 256 to get a difference value. The difference value is multiplied by the fractional portion of the input value, and then added to the original sampling value to find the final sampling value.
index=min(floor(input * 256), 255); samplingValue=LUT[index] + LUT[index + 256] * (input * 256 - index);
If the range of input values is between –1.0
and 1.0
, each input value is multiplied by 128 and then its integer portion is converted to a two's complement index. The index is first used to get a sampling value from the lookup table, and then it is incremented by 256 to get a difference value. The difference value is multiplied by the fractional portion of the input value, and then added to the original sampling value to find the final sampling value.
if (input < 0.0) { flooredInput=floor(input * 128); index = 255 + flooredInput; samplingValue=LUT[index] + LUT[index + 256] * (input * 128 - flooredInput); } else { index=min(floor(input * 128), 127); samplingValue=LUT[index] + LUT[index + 256] * (input * 128 - index); }
Figure 12-3 shows the order that sampling values are stored in.
Lookup tables are usually created by storing the sampling value that results from dividing the index by 256 or 128. The difference between each sampling value and the next is stored 256 indices later. Note that the lookup table is discontinuous for input values between –1.0
and 1.0
.
The following pseudocode samples illustrate this process using func
as the function that calculates sampling values from input values.
for (i = 0; i < 256; i++) LUT[i] = func((float) i / 256.0f); for (i = 0; i < 255; i++) LUT[i + 256] = LUT[i + 1] - LUT[i]; LUT[511] = func(1.0f) - LUT[255] * 16.0f / 15.0f;
The difference value for an input of 1.0
(the last difference element) is multiplied by 16.0/15.0 because the GPU has a fractional precision of 4 bits. If the original difference value were stored, the highest input value would not produce the sampling value for 1.0
.
for (i = 0; i < 128; i++) { LUT[i] = func((float) i / 128.0f); LUT[255 - i] = func((float) (i + 1) * -1.0f / 128.0f); } for (i= 0; i < 127; i++) { LUT[i + 256] = LUT[i + 1] - LUT[i]; LUT[i + 384] = LUT[i + 129] - LUT[i + 128]; } LUT[383] = func(1.0f) - LUT[127] * 16.0f / 15.0f; LUT[511] = LUT[0] - LUT[255];
In this example, the value is multiplied by 16.0/15.0 because, if the original difference value were stored, the highest input value would not produce the sampling value for 1.0.
As explained in the lighting equations, after you have created a lookup table and bound it to a texture type for lookup tables, load it with the glTexImage1D
function and set its lookup table number in a reserved uniform to allow calculations to access it. Note that the glUniform1i
function sets the lookup table number, not the texture ID, in the reserved uniform.
glBindTexture(GL_LUT_TEXTURE2_DMP, lutTextureID); glTexImage1D(GL_LUT_TEXTURE2_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, LUT); glUniform1i(glGetUniformLocation(progID, "dmp_LightEnv. samplerRR"), 2);
12.2.11. Lookup Table Input
All terms (D0, D1, RR, RG, RB, FR, SP), except for the distance attenuation of light (DA), take as input the cosine of the angle between two vectors (the dot product of two normalized vectors).
The input vectors are the normal vector (N), light vector (L), view vector (V), half vector (H), tangent vector (T), binormal vector (B), spotlight direction vector, and the projection of the half vector onto the tangent plane.
To specify the values used as lookup table input for each factor, set the reserved uniforms for the lighting environment, dmp_LightEnv.lutInputXX
(where XX
is D0
, D1
, RR
, RG
, RB
, FR
, or SP
), to one of the following values using the glUniform1i
function. The cosine of the angle between the two specified vectors is used as lookup table input for each factor.
Value |
Input Vector Pair |
---|---|
|
The normal and half vectors (default). |
|
The view and half vectors. |
|
The normal and view vectors. |
|
The light and normal vectors. |
|
The inverse light vector and the spotlight vector (cannot be used with RR, RG, RB, and FR). |
|
The tangent vector and the projection of the half vector onto the tangent plane (cannot be used with RR, RG, RB, and FR). |
12.2.12. Distance Attenuation of Light
The following equation calculates input values for the distance attenuation of light.
fposition is the fragment position and lposition is the light position. Both are expected to use eye coordinates. Position only has meaning for a point light source. It means nothing for a directional light source.
This equation shows that the distance between a fragment and a light is multiplied by a scale value, and then added to a bias value to calculate an input value for the distance attenuation of light.
Create lookup tables that take input between 0.0
and 1.0
(absolute values). Use the glUniform1f
function to set the scale value in the reserved uniform dmp_FragmentLightSource[i].distanceAttenuationScale
and the bias value in dmp_FragmentLightSource[i].distanceAttenuationBias
.
When GL_LIGHT_ENV_LAYER_CONFIG7_DMP
is specified as the layer configuration’s setting value, you must disable distance attenuation. Set dmp_FragmentLightSource[i].distanceAttenuationEnabled
to GL_FALSE
.
12.2.13. Texture Combiner Settings
The primary and secondary colors calculated by fragment lighting can each be used as an input source to a texture combiner.
To use them, set the reserved uniforms for the input source (dmp_TexEnv[i].srcRgb
and dmp_TexEnv[i].srcAlpha
) to GL_FRAGMENT_PRIMARY_COLOR_DMP
for the primary color, and to GL_FRAGMENT_SECONDARY_COLOR_DMP
for the secondary color.
The output value is (0.0, 0.0, 0.0, 1.0) when lighting is disabled (when dmp_FragmentLighting.enabled
is GL_FALSE
).
12.3. Bump Mapping
Bump mapping is a feature of fragment lighting that perturbs (alters) a fragment's normal and tangent vectors according to a normal map that is input as a texture. Bump mapping can make an object appear to have shadows caused by surface irregularities. This allows you to render a simple model that looks complex but actually has a small polygon count.
12.3.1. Reserved Uniform
The following reserved uniforms are used for bump mapping.
Normal Maps
For the normal map texture for bump mapping (dmp_LightEnv.bumpSelector
), specify the texture unit to which the texture is bound. A normal map texture is created with the x, y, and z components of the perturbation vectors encoded in the R, G, and B components, respectively. A vector value of -1.0
is encoded as the minimum luminance (0 in an 8-bit format), and 1.0
is encoded as the maximum luminance (255 in an 8-bit format).
Perturbation Mode
To enable bump mapping, set the perturbation mode (dmp_LightEnv.bumpMode
) to any value other than GL_LIGHT_ENV_BUMP_NOT_USED_BUMP
. The following table shows the perturbation modes.
Perturbation Mode |
Perturbed Vectors |
---|---|
|
None. |
|
Normal vectors (bump mapping). |
|
Tangent vectors (tangent mapping). |
Normal Recalculation
If dmp_LightEnv.bumpRenorm
is set to GL_TRUE
to enable recalculation of normal vectors, the z-component of the normal vector is not obtained from the B component sampled from a texture. Instead, the z-component is recalculated from the x-component and the y-component.
Note: If the expression inside the square root is negative, the result is
0
.
In most cases, recalculating values yields better results than sampling them from a texture. This recalculation feature must be enabled, if bump mapping (of normals) uses a texture that only has R and G components (GL_HILO8_DMP
). However, if you have selected tangent mapping as the perturbation mode for use with a technique such as anisotropic reflections, we recommend that you avoid using this feature. This is because tangent mapping (for fragment lighting) expects input perturbation tangents that do not have a z-component. If the recalculation feature is enabled, nonzero values may be generated in the z-component.
If recalculation is disabled, the perturbation normal vectors sampled from the texture are not normalized before they are used. Make sure that you normalize values before storing them in a texture. Re-enable normal calculation when point sampling (GL_NEAREST
) is not configured as the texture filter mode because filtering can cause the non-normalized values to be used as the perturbation normals.
Reserved Uniform |
Type |
Setting Value |
---|---|---|
|
|
Specifies the texture unit to use for the normal map.
|
|
|
Specifies the perturbation mode for normal and tangent vectors.
|
|
|
Specifies whether to regenerate the third component of the normal vector. Specify |
12.4. Shadows
3DS shadows are rendered in two passes. First, the shadow accumulation pass creates a shadow buffer (that contains the scene's depth values taking the light source as the origin), which is then referenced by the shadow lookup pass to cast shadows. The shadow intensity information collected, along with the depth values in the first pass, allows you to represent soft shadows.
12.4.1. Shadow Accumulation Pass
The shadow accumulation pass requires that the fragment operation mode (dmp_FragOperation.mode
) be switched to shadow mode (GL_FRAGOP_MODE_SHADOW_DMP
), and that the shadow information (depth values and shadow intensity) be stored in a shadow texture (with a format of GL_SHADOW_DMP
and a type of GL_UNSIGNED_INT
). Note that only texture unit 0 (GL_TEXTURE0
) can write shadow information to a shadow texture. Also note that mipmaps cannot be applied to shadow textures.
When the fragment pipeline switches to shadow mode, shadow information is output to the attachment point for the color buffer rather than the depth or stencil buffer. As a result, a shadow texture must be attached to the color buffer's attachment point (GL_COLOR_ATTACHMENT0
). Render targets attached to depth and stencil attachment points are ignored in shadow mode. The alpha and stencil tests are skipped.
You can use the following procedure to create a shadow texture and specify a render target.
glActiveTexture(GL_TEXTURE0); glGenTextures(1, &shadowTexID); glBindTexture(GL_TEXTURE_2D, shadowTexID); glTexImage2D(GL_TEXTURE_2D, 0, GL_SHADOW_DMP, shadowWidth, shadowHeight, 0, GL_SHADOW_DMP, GL_UNSIGNED_INT, 0); glGenFramebuffers(1, &shadowFboID); glBindFramebuffer(GL_FRAMEBUFFER, shadowFboID); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shadowTexID, 0);
Shadow information is accumulated using the coordinate system of the light source. Shadow information comprises the depth from the light source (the depth values) and the shadow intensity. When rendering shadows, there are no shadows wherever the G component of the color information is 1.0
(the R, B, and A components have no effect), there are opaque hard shadows wherever the G component of the color information is 0.0
, and there are non-opaque shadows (soft shadows) everywhere else.
When opaque hard shadows are rendered, only the shadow depth values are updated. The shadow intensity does not change. The depth of a fragment and its corresponding pixel in the shadow buffer are compared (using GL_LESS
). If the fragment has a smaller value, the depth value in the shadow buffer is updated.
When non-opaque soft shadows are rendered, only the shadow intensity is updated. The shadow depth values do not change. The depth of a fragment and its corresponding pixel in the shadow buffer are compared (using GL_LESS
). If the fragment has a smaller value, the shadow intensity is also compared (using GL_LESS
) and then, if the fragment still has a smaller value, the shadow intensity in the shadow buffer is updated.
When you initialize the color buffer in the shadow accumulation pass you must set the clear color to (1.0, 1.0, 1.0, 1.0) by using the glClearColor
function and specify GL_COLOR_BUFFER_BIT
to the glClear
function. Note that you must set all color components (R, G, B, and A)—not just the G component—equal to 1.0
in the clear color.
The shadow lookup pass is processed in eye coordinates, so the depth values must be created by using linear interpolation in eye space. (In most cases this differs from OpenGL, which uses non-linear relationships.) As a result, you must set a value in the reserved uniform for the w-buffer's scale factor (dmp_FragOperation.wScale
), using the glUniform1f
function. This has an initial value of 0.0
, which results in the same non-linear relationship as OpenGL. Depth values have a lower valid precision around the far clipping plane, when the near clipping plane is close to the viewpoint. To use linear interpolation, given f as the clip value for the far clipping plane, set the scale factor to 1.0/f
for a perspective projection or to 1.0
for an orthographic projection.
An object casts a hard shadow if it is rendered with a G color component of 0.0
. This is generally implemented by disabling textures, and then implementing a vertex shader that outputs those vertex colors with G components of 0.0
. Every other rendered color is treated as a soft shadow.
Several rendering passes may be necessary to accumulate the required shadow information. Information for non-opaque shadows must be accumulated after information for opaque shadows. Results are not guaranteed if information is accumulated in the opposite order or in alternating order. When light sources do not move, you can generate shadow textures more efficiently by rendering motionless objects alone to a shadow texture ahead of time. Simplifying object shapes and decreasing polygon counts are also effective ways to improve performance.
Texture unit 0 and the texture combiners are configured as follows, when shadows are rendered using a vertex shader implementation that outputs vertex colors.
glUniform1i(glGetUniformLocation(progID, "dmp_Texture[0].samplerType"), GL_FALSE); glUniform1i(glGetUniformLocation(progID, "dmp_TexEnv[0].combineRgb"), GL_REPLACE); glUniform1i(glGetUniformLocation(progID, "dmp_TexEnv[0].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].srcRgb"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].srcAlpha"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR);
12.4.2. Shadow Lookup Pass
The shadow lookup pass requires that the fragment operation mode be switched to normal mode (GL_FRAGOP_MODE_GL_DMP
), and that texture unit 0 (GL_TEXTURE0
) be configured to reference the shadow texture that has accumulated the shadow information. To reference a shadow texture, you must set the reserved uniform dmp_TexEnv[0].samplerType
to GL_TEXTURE_SHADOW_2D_DMP
(which specifies shadow textures), and bind the texture that has accumulated shadow information to GL_TEXTURE_2D
. When fragment lighting is enabled, its primary and secondary colors are used as texture combiner input. Otherwise, vertex colors and output from texture unit 0 are used.
The reserved uniform dmp_Texture[0].perspectiveShadow
indicates whether a perspective projection or orthographic projection was applied when the shadow accumulation pass was run. Specify GL_TRUE
for perspective projection or GL_FALSE
for orthographic projection.
glUniform1i(glGetUniformLocation(progID, "dmp_Texture[0].samplerType"), GL_TEXTURE_SHADOW_2D_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_Texture[0].perspectiveShadow"), GL_TRUE); glUniform1i(glGetUniformLocation(progID, "dmp_TexEnv[0].combineRgb"), GL_MODULATE); glUniform1i(glGetUniformLocation(progID, "dmp_TexEnv[0].combineAlpha"), GL_MODULATE); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].srcRgb"), GL_TEXTURE0, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].srcAlpha"), GL_TEXTURE0, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); glUniform1i(glGetUniformLocation(progID, "dmp_FragOperation.mode"), GL_FRAGOP_MODE_GL_DMP); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, shadowTexID);
Both 3DS and OpenGL reference shadow information from textures. The texture unit compares texture coordinates and shadow information obtained from texels. For this to work properly, the correct texture coordinates must be specified. Note that a shadow texture is referenced using texture coordinates (s/q, t/q, r/q) in OpenGL, and (s/r, t/r, r - bias) on 3DS. The texture transformation matrix has already been applied to texture coordinates (s, t, r, q), and the reserved uniform dmp_Texture[0].shadowZBias
specifies the bias value. If shadow information is accumulated by an orthographic projection, texture coordinates s and t are referenced directly. A perspective projection, however, requires adjustments to the texture transformation matrix and bias value.
The following texture transformation matrix and bias value could be used to compare the texture coordinates and shadow information accumulated by a perspective projection.
Texture Transformation Matrix and Bias for Perspective Projection
Texture Transformation Matrix and Bias for Parallel Projection
n is the clip value at the near clipping plane, f is the clip value at the far clipping plane, and r and t are the right and top side values of the frustum.
When calculating the value that is compared to the shadow buffer depth (that is, r – bias ), if the texture coordinate r is not within the range from 0.0
to 1.0
, r is clamped to between 0.0
and 1.0
before bias is subtracted. To compare values correctly, specify a bias of 0
for any objects placed beyond the far clipping plane in the coordinate system of the light source used during the shadow accumulation pass.
You can create a texture transformation matrix with the following OpenGL code.
glMatrixMode(GL_TEXTURE); glLoadIdentity(); // (glFrustum(-r, r, -t, t, n, f)) glFrustumf(r/n, -3r/n, t/n, -3t/n, 1.0f, 0.0f); glScalef(-1.0f/(f-n), -1.0f/(f-n), -1.0f/(f-n)); // (glOrtho(-r, r, -t, t, n, f)) glOrthof(-3r, r, -3t, t, 2n-f, f);
By setting the border color and texture wrapping mode, you can control the sampling results for texture coordinates that are less than 0.0
or greater than 1.0
. The texture wrapping mode guarantees that when GL_CLAMP_TO_BORDER
is configured for the s and t texture coordinates, out-of-range sampling values are set to the border color (which must have a value of 0.0
or 1.0
for all components). Sampling results are undefined in the current implementation if the wrapping mode is not GL_CLAMP_TO_BORDER
or if the border color is neither (0.0, 0.0, 0.0, 0.0) nor (1.0, 1.0, 1.0, 1.0).
glBindTexture(GL_TEXTURE_2D, shadowTexID); GLfloat bcolor[] = {1.0f, 1.0f, 1.0f, 1.0f}; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bcolor); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);
If the depth values in light source coordinates are greater than the depth values in shadow texels, the shadow intensity will be 0.0
after the comparison. Otherwise, the shadow intensity in the shadow texels is used. Texture units output the shadow intensity as a value for each RGBA component.
12.4.3. Omnidirectional Shadow Mapping
You can combine cube mapping with shadow textures to implement omnidirectional shadow mapping.
This implementation requires that you bind a cube map texture (one of six types: GL_TEXTURE_CUBE_MAP_POSITIVE_{X,Y,Z}
or GL_TEXTURE_CUBE_MAP_NEGATIVE_{X,Y,Z}
) to the texture unit during the shadow accumulation pass, render six times, and then specify GL_TEXTURE_SHADOW_CUBE_DMP
as the texture to reference during the shadow lookup pass (dmp_Texture[0].samplerType
). Also, you must specify GL_CLAMP_TO_EDGE
as the texture coordinate wrapping mode to use in both the S and T directions.
12.4.4. Soft Shadows Using the Silhouette Shader
Shadow textures contain depth values and shadow intensity. The sampling value output by a texture unit that references a shadow texture is either the color black (0.0, 0.0, 0.0), within a shadow region, or the shadow intensity saved in the shadow texture (between 0.0 and 1.0), outside of a shadow region. You can represent soft shadows by setting this shadow intensity to the appropriate values.
The 3DS silhouette shader can render silhouette primitives (edges) as soft shadow regions, whose shadow intensity changes gradually until they disappear. Opaque hard shadows are rendered before the silhouette shader is used for rendering. Set the G component of the color at the silhouette edge to 1.0
, and set the G component of the vertex color output by the vertex shader to 0.0
. All other settings are the same as those used to render opaque hard shadows. This renders the rectangle for a silhouette edge so that its G component gradually changes from 0.0
on the object side to 1.0
on the outer side.
During the shadow accumulation pass for these soft shadow regions, you can apply additive modulation to the shadow intensity, according to the relative distance to an object. This is called an attenuation factor, and it can adjust the width of the soft shadow region through the reserved uniforms for the soft shadow bias (dmp_FragOperation.penumbraBias
) and scale (dmp_FragOperation.penumbraScale
). By adjusting these values, you can represent more natural soft shadows that narrow close to the object.
The attenuation factor is calculated by the following equation. The equation uses Zfrag to represent a fragment's depth value and Zrec to represent the depth value stored in a shadow texture. The shadow intensity is not attenuated properly if objects have not already been rendered and depth values have not already been saved in a shadow texture.
12.4.5. Handling Shadow Artifacts
12.4.5.1. Self-Shadow Aliasing
Various problems can occur during multiple-pass shadow rendering. For example, by accidentally casting a shadow on itself (called self-shadow aliasing) a fragment can cause a moiré pattern to be rendered. This occurs when depth values from the shadow accumulation pass are slightly smaller than depth values from the shadow lookup pass (measured from the light source). Unnatural shadows such as these are called shadow artifacts.
One way to suppress shadow artifacts is to apply a negative offset (bias) to depth values during the shadow lookup pass. You can set the bias value in the reserved uniform dmp_Texture[0].shadowZBias
.
glUniform1f(glGetUniformLocation(progID, "dmp_Texture[0].shadowZBias"), 1.2f*n/(f-n));
Silhouette Shadow Artifacts
As explained earlier, the shadow intensity in a shadow texture is output as a sampling value for any location that is determined to be outside of a shadow region during the shadow lookup pass. Although non-shadow regions usually have a shadow intensity of 1.0
and no brightness attenuation, the soft shadow regions rendered by the silhouette shader can have an output shadow intensity that is not 1.0
, and thereby have brightness attenuation. This causes shadow artifacts to occur for some objects.
The method used to suppress self-shadow aliasing does not work for silhouette shadow artifacts, but these artifacts can still be suppressed through adjustments to shadow settings and texture combiner settings.
Because the main cause of these artifacts is the brightness attenuation of a light source by soft shadows on a parallel plane, the shadow texture output (shadow attenuation) can be calculated by using the following equation.
ShadowAttenuation = 1.0 – f (1.0 – ShadowIntensity)
Where f is a function that returns a value close to 0.0
when the fragment normals are perpendicular to the light source and 1.0
when the fragment normals are parallel to the light source.
This equation yields a shadow attenuation of 1.0
(shadows have no effect) when a fragment's normals are nearly perpendicular to the light source and yields the shadow intensity itself when the normals are nearly parallel to the light source.
This shadow attenuation term can be implemented by using the Fresnel factor (lookup table FR) as the f function, reserved uniforms for the lighting environment (dmp_LightEnv.shadowAlpha
and dmp_LightEnv.invertShadow
) as the inverse of the shadow intensity, and texture combiner settings as the inverse of the final value.
Lighting is configured as follows. Note that the Fresnel factor affects only the alpha component. The alpha component is multiplied by the texture combiners. The f function is configured to return the square of the input value.
glUniform1i(glGetUniformLocation(progID, "dmp_FragmentLighting.enabled"), GL_TRUE); // ..other code.. glUniform1i(glGetUniformLocation(progID, "dmp_LightEnv.lutInputFR"), GL_LIGHT_ENV_LN_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG1_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_LightEnv.fresnelSelector"), GL_LIGHT_ENV_PRI_SEC_ALPHA_FRESNEL_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_LightEnv.shadowAlpha"), GL_TRUE); glUniform1i(glGetUniformLocation(progID, "dmp_LightEnv.invertShadow"), GL_TRUE); GLfloat lut[512]; int j; memset(lut, 0, sizeof(lut)); for (j = 1; j < 128; j++) { lut[j] = powf((float)j/127.0f, 2.0f); lut[j+255] = lut[j] - lut[j-1]; } glTexImage1D(GL_LUT_TEXTURE0_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); glUniform1i(glGetUniformLocation(progID, "dmp_FragmentMaterial.samplerFR"), 0);
In this code, f (1.0 – ShadowIntensity) is output as the fragment's primary and secondary alpha component.
The final shadow attenuation factor is calculated next from the texture combiner settings and is multiplied by the fragment's primary color. Note that the fragment's primary alpha value is inverted here (GL_ONE_MINUS_SRC_ALPHA
).
glUniform1i(glGetUniformLocation(progID, ("dmp_TexEnv[0].combineRgb"), GL_MODULATE); glUniform1i(glGetUniformLocation(progID, ("dmp_TexEnv[0].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(progID, ("dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(progID, ("dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(progID, ("dmp_TexEnv[0].srcRgb"), GL_FRAGMENT_PRIMARY_COLOR_DMP, GL_FRAGMENT_PRIMARY_COLOR_DMP, GL_PRIMARY_COLOR); glUniform3i(glGetUniformLocation(progID, ("dmp_TexEnv[0].srcAlpha"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR);
12.4.6. Reserved Uniform
The following table lists the reserved uniforms used for shadows.
Reserved Uniform |
Type |
Setting Value |
---|---|---|
|
|
Specifies the type of texture to reference. The following two types can be used for shadows.
|
|
|
Specifies whether a perspective projection has been applied as the projection transformation. Specify |
|
|
Specifies the bias value for the negative offset to apply to depth values during the lookup pass. 0.0 (default) |
|
|
Specifies the fragment operation mode. Specifies shadow mode during the accumulation pass and standard mode during the lookup pass.
|
|
|
Specifies the scale factor for the w-buffer. 0.0 (default) |
|
|
Specifies the scale value for soft shadows. 1.0 (default) |
|
|
Specifies the bias value for soft shadows. 0.0 (default) |
12.4.7. Checking Shadow Texture Content
To check the image that was rendered to a shadow texture, attach the shadow texture to the current color buffer, and then read the texel data by calling the glReadPixels
function and specifying GL_RGBA
for the format
parameter and GL_UNSIGNED_BYTE
for the type
parameter. When accessed through a u32
pointer, the least-significant 8 bits of texel data store the shadow intensity, and the most-significant 24 bits store the depth value. Shift this to the right by 8 bits to get only the depth value.
Because shadow textures store an 8-bit shadow intensity and a 24-bit depth value, and both of these represent values in the range from 0.0
to 1.0
, the shadow intensity and depth value have different precisions.
The shadow intensity takes a value between 0x00
and 0xFF
, where 0xFF
indicates the absence of a soft shadow region and all other values indicate the presence of a soft shadow region. In other words, a value of 0x00
indicates a shadow intensity of 0.0 and 0xFF
indicates a shadow intensity of 1.0.
The depth value is scaled so that 0x000000
is the near value and 0xFFFFFF
is the far value. In other words, a value of 0x000000
indicates a depth value of 0.0 and 0xFFFFFF
indicates a depth value of 1.0. Note that this scaling is uniform if the w buffer is enabled during the shadow accumulation pass. For more information, see 10.3.3. W-Buffer.
A shadow intensity of 0xFF
and a depth value of 0xFFFFFF
are written to the shadow texture wherever there are no shadows. A shadow intensity of 0xFF
and a depth value other than 0xFFFFFF
are written to the shadow texture wherever there are only hard shadows. A shadow intensity other than 0xFF
and a depth value other than 0xFFFFFF
are used in regions that have both hard and soft shadows.
Shadow Type |
Shadow Intensity |
Depth Values |
---|---|---|
No shadows |
0xFF |
0xFFFFFF |
Only hard shadows |
0xFF |
Value other than 0xFFFFFF |
Soft shadows |
Value other than 0xFF |
Value other than 0xFFFFFF |
12.5. Fog
Although 3DS fog has nearly the same features as fog defined in OpenGL ES 1.1, the effect of 3DS fog is determined by projection-corrected depth values, whereas the effect of OpenGL fog is determined by the distance from the viewpoint. Another difference is that fog coefficients are specified by lookup tables. There are also no settings for fog properties, such as the beginning, end, and density.
As in OpenGL, the following equation determines a fragment's color after fog has been applied.
Color = f × Cfragment + (1 - f) × Cfog
Where f is the fog coefficient (0.0–1.0).
12.5.1. Reserved Uniform
The following reserved uniforms are used for fog.
Fog Mode
To enable fog, specify a fog mode (dmp_Fog.mode
) of GL_FOG
using the glUniform1i
function.
To disable it, specify GL_FALSE
.
Fog Color
Use the glUniform3f
function to set the fog color (dmp_Fog.color
). Only the RGB components are set. (The alpha component is not.)
Fog Coefficients
Fog coefficients are specified by a lookup table that takes depth values in window coordinates as input. Call the glUniform1i
function to set the reserved uniform dmp_Fog.sampler
to the lookup table number to use.
Specify the table's number rather than GL_LUT_TEXTUREi_DMP
.
Whether to Invert Input Depth Value
You can choose whether to invert input values (changing z to 1 - z) for the fog coefficient lookup table. To invert values, set the reserved uniform dmp_Fog.zFlip
to GL_TRUE
, using the glUniform1i
function.
Reserved Uniform |
Type |
Setting Value |
---|---|---|
|
|
Specifies the mode for processing the fog pipeline (the fog mode).
|
|
|
Specifies the fog color. There is no alpha component. Each component has a value between 0.0 and 1.0. (0.0, 0.0, 0.0) by default. |
|
|
Specifies whether to invert the depth values used as input to the lookup table for fog coefficients.
|
|
|
Specifies the lookup table to use for fog coefficients. 0 to 31 |
12.5.2. Creating and Specifying Lookup Tables
A lookup table for fog coefficients takes input values between 0.0
and 1.0
, and has a fixed width
of 256
. Like other lookup tables, it stores the output values in the first 128 elements and the differences between output values in the last 128 elements.
You must be careful about how you convert input values, when you create lookup tables to implement OpenGL fog coefficients on a 3DS system. The input depth values are specified in window coordinates, with the near clipping plane at the minimum value (0.0
) and the far clipping plane at the maximum value (1.0
). For a perspective projection, however, these depth values have a non-linear relationship with depth values in eye coordinates. As a result, lookup table output must be calculated using depth values that were converted from window coordinates into eye coordinates.
Considering that input in window coordinates (between 0.0
and 1.0
) is mapped to clip coordinates (between 0.0
and –1.0
), the following equation is used to convert values into eye coordinates. Note that the sign of the input is reversed.
(Xe Ye Ze We) = (0.0 0.0 -Zw 1.0) × Mprojection-1
Fog coefficients are a function of the distance to a fragment from the origin in eye coordinates. Input to this function can be approximated as the distance -Ze / We between the xy plane and the fragment in eye coordinates.
This is shown by the following sample code. FogCoef
is the fog coefficient function.
float Fog_LUT[256], Fog_c[128 + 1]; int i; Matrix44 invPM; Vector4 v_eye, v_clip(0.0f, 0.0f, 0.0f, 1.0f); MTX44Inverse(&invPM, &projMatrix); Vector4 v0(invPM.m[0]); Vector4 v1(invPM.m[1]); Vector4 v2(invPM.m[2]); Vector4 v3(invPM.m[3]); for (i = 0; i <= 128; i++) { v_clip.z = -(static_cast<f32>(i)) / 128; v_eye.x = VEC4Dot(&v0, &v_clip); v_eye.y = VEC4Dot(&v1, &v_clip); v_eye.z = VEC4Dot(&v2, &v_clip); v_eye.w = VEC4Dot(&v3, &v_clip); Fog_c[i] = -(v_eye.z / v_eye.w); } for (i = 0; i < 128; i++) { Fog_LUT[i] = FogCoef(Fog_c[i]); Fog_LUT[128 + i]= FogCoef(Fog_c[i + 1]) - FogCoef(Fog_c[i]); }
OpenGL fog is affected by the eye coordinates' z value, but 3DS fog is affected by depth values that have been corrected with a perspective projection. As a result, fog changes when the near and far clipping planes change, and the same lookup table can produce different effects depending on whether the w-buffer or a normal depth buffer is used. For more information, see 10.3.3. W-Buffer.
12.6. Gas Rendering
Gas rendering uses the fog feature, configured in gas mode, to render gaseous bodies from density information, which is itself generated based on depth values of polygon objects. Gaseous bodies require three rendering passes: the polygon object rendering pass, which generates depth values for the polygon objects; the density rendering pass, which accumulates density information in a gas texture; and the shading pass, which renders gaseous bodies by referencing a gas texture.
12.6.1. Polygon Object Rendering Pass
This pass renders polygon objects as usual, generating depth information that is used to determine where the polygon objects and gaseous bodies intersect. The content of the depth buffer is the only rendering result that is used by the next pass.
12.6.2. Density Rendering Pass
This pass uses point sprites, or some other means, to render the smallest units comprising a gaseous body and gas particles (a texture with a density pattern), and it accumulates a gas's depth information in the color buffer.
Preparing Color Buffers and Gas Textures
Prepare a color buffer with an internal format of GL_GAS_DMP
and a texture (gas texture), both of which are used to copy the rendering results. Create the color buffer with the same size as the depth buffer, but the gas texture must have a width and height (in texels) that are both powers of 2, and GL_UNSIGNED_SHORT
must be specified for type
. Because gas textures always use point sampling, minification and magnification filters have no effect. Use GL_NEAREST
as the filter setting. Note that mipmaps cannot be applied to gas textures.
// Generating Object glGenFramebuffers(1, &gasAccFboID); glGenTextures(1, &gasTexID); // Renderbuffer & Framebuffer glGenRenderbuffers(1, &gasAccColorID); glBindRenderbuffer(GL_RENDERBUFFER, gasAccColorID); glRenderbufferStorage(GL_RENDERBUFFER, GL_GAS_DMP, GAS_ACC_WIDTH, GAS_ACC_HEIGHT); glBindFramebuffer(GL_FRAMEBUFFER, gasAccFboID); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gasAccColorID); // Gaseous Texture glBindTexture(GL_TEXTURE_2D, gasTexID); glTexImage2D(GL_TEXTURE_2D, 0, GL_GAS_DMP, GAS_TEX_WIDTH, GAS_TEX_HEIGHT, 0, GL_GAS_DMP, GL_UNSIGNED_SHORT, 0); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
Rendering Gas Particles
When the fragment pipeline is switched and gas mode (GL_FRAGOP_MODE_GAS_ACC_DMP
) is configured as the fragment operation mode, density information is rendered to a color buffer. Two types of density information are accumulated in the color buffer: information that is simply accumulated (D1), and information that accounts for intersections with polygon offsets (D2).
Disable the depth test, the depth mask, blending, and fog to prevent the content of the depth buffer from being updated. However, do not change the comparison function that was used for the depth test, when polygon objects were rendered.
Clear only one buffer, the color buffer to which density information is rendered. The depth buffer must not be cleared.
The R component of the gas particles to be rendered (Df) is accumulated in the color buffer as density information. D1 and D2 are updated to D1' and D2' by the following equations. The equation for D2' depends on the comparison function for the depth test.
Zb is the depth value stored in the depth buffer, and Zf is the depth value of the fragment.
D1 accumulates fragment (gas particle) density information unchanged. D2 accumulates fragment density information that has been multiplied by the attenuation coefficient EZ in the depth direction and also by the difference between the depth values in the depth buffer and fragment. The EZ coefficient is a floating-point number set by the glUniform1f
function in the reserved uniform dmp_Gas.deltaZ
.
// Change to gas accumulation mode. glBindFramebuffer(GL_FRAMEBUFFER, gasAccFboID); glUniform1i(glGetUniformLocation(progID, "dmp_FragOperation.mode"), GL_FRAGOP_MODE_GAS_ACC_DMP); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glDisable(GL_BLEND); glUniform1i(glGetUniformLocation(progID, "dmp_Fog.mode"), GL_FALSE); // Colorbuffer Clear glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); // Set dmp_Gas.deltaZ. glDepthFunc(GL_LESS); glUniform1f(glGetUniformLocation(progID, "dmp_Gas.deltaZ"), 50.0f);
Copying Information to a Gas Texture
After all gas particles have been rendered, the density information accumulated in the color buffer is copied to a gas texture.
The gas texture is not usually allocated with the same size as the color buffer (the width and height must be powers of 2), so the glCopyTexSubImage2D
function partially copies color buffer data. The gas texture is required for the next pass.
// Bind and copy to gaseous texture. glBindTexture(GL_TEXTURE_2D, gasTexID); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, GAS_ACC_WIDTH, GAS_ACC_HEIGHT);
12.6.3. Shading Pass
This pass shades gaseous bodies by referencing the density information accumulated in a gas texture, and then blends the shading results with the color buffer contents from the polygon object rendering pass.
Gaseous bodies are shaded using the fog feature configured in gas mode. Special settings are required for fog input from the texture combiners.
Fog Input (Texture Combiner Settings)
Fog takes two inputs: the RGBA components from the second-to-last texture combiner (though only the R component is actually used), and the density information from the input source, specified as input source 2 (the third component of srcRgb
and srcAlpha
) to the last texture combiner (the specified texture unit must sample the gas texture). Because output from the last texture combiner is ignored, the number of usable texture combiner levels is reduced by one.
Ultimately, fog output is blended with the contents of the color buffer. Because fog outputs alpha values that account for intersections with polygon objects, blending both outputs based on the fog's alpha values allows gaseous bodies to be rendered in the correct order front-to-back.
Fog Operations in Gas Mode
In gas mode, fog shades gaseous bodies based on input information.
The RGB components of the shading results are determined by shading lookup tables. To determine the alpha component, the gas attenuation value (dmp_Gas.attenuation
) is multiplied by density information that accounts for intersections (D2), and the result is looked up in the lookup table specified by the fog coefficient (dmp_Fog.sampler
).
Shading Lookup Tables
Shading lookup tables accept either density information or shading intensity as input, and give the RGB components of the shading results as output.
Shading lookup tables are specified separately for each component using tables generated with width
set to 16
. Both the input and output values are between 0.0
and 1.0
. As with other lookup tables, the output values are stored in the first eight elements, the differences between output values are stored in the last eight elements, and the output values are interpolated using the delta values.
To specify a lookup table, set its number in the following reserved uniforms, using the glUniform1i
function.
Reserved Uniform |
Type |
Setting Value |
---|---|---|
|
|
Specifies the lookup tables to use as the shading lookup tables (for the R, G, and B components). 0 to 31 |
The following are examples of shading lookup tables and their implementations.
// Define GLfloat shading_color[3 * 9] = { 0.00f, 0.00f, 0.00f, 0.20f, 0.15f, 0.05f, 0.60f, 0.25f, 0.15f, 0.90f, 0.35f, 0.20f, 0.92f, 0.60f, 0.15f, 0.95f, 0.85f, 0.05f, 1.00f, 0.95f, 0.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f }; GLfloat samplerTR[16], samplerTG[16], samplerTB[16]; // Table for(int i = 0; i < 8; i++) { // Shading color value samplerTR[i] = shading_color[3*i + 0]; samplerTG[i] = shading_color[3*i + 1]; samplerTB[i] = shading_color[3*i + 2]; // Difference of shading color value samplerTR[8 + i] = shading_color[3*(i + 1) + 0] - shading_color[3*i + 0]; samplerTG[8 + i] = shading_color[3*(i + 1) + 1] - shading_color[3*i + 1]; samplerTB[8 + i] = shading_color[3*(i + 1) + 2] - shading_color[3*i + 2]; } // Texture glBindTexture(GL_LUT_TEXTURE1_DMP, samplerTR_ID); glTexImage1D(GL_LUT_TEXTURE1_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, samplerTR); glBindTexture(GL_LUT_TEXTURE2_DMP, samplerTG_ID); glTexImage1D(GL_LUT_TEXTURE2_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, samplerTG); glBindTexture(GL_LUT_TEXTURE3_DMP, samplerTB_ID); glTexImage1D(GL_LUT_TEXTURE3_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, samplerTB); // Set Uniform glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTR"), 1); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTG"), 2); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTB"), 3);
Shading Based on Density Information
For shading that is based on density information alone (without accounting for the effect of shadows cast by light), set the reserved uniform dmp_Gas.colorLutInput
to GL_GAS_DENSITY_DMP
, using the glUniform1i
function to select density information, as the shading lookup table input.
As explained for the density rendering pass, two types of density information are stored in a gas texture. One type of density information does not account for intersections with polygon objects (D1), and the other type does (D2). You can set a value in the reserved uniform dmp_Gas.shadingDensitySrc
to select the density information to use.
Specify GL_GAS_PLAIN_DENSITY_DMP
for D1 and GL_GAS_DEPTH_DENSITY_DMP
for D2, using the glUniform1i
function. Even if you choose D1 as input to the shading lookup tables, fog in gas mode outputs alpha components calculated from density information that accounts for intersections (D2). By using alpha values while blending, polygon objects and gaseous bodies can be rendered with the correct front-to-back ordering.
Lookup table input uses values between 0.0
and 1.0
. You can multiply the density information by the reciprocal of the maximum density value to keep values in this range. The reciprocal of the maximum value can be calculated automatically. By setting the reserved uniform dmp_Gas.autoAcc
to GL_TRUE
, you can use the glUniform1i
function to calculate the reciprocal from the maximum D1 value in the density rendering pass.
When GL_FALSE
is specified, the value set in the reserved uniform dmp_Gas.accMax
by the glUniform1f
function is used as the reciprocal of the maximum value.
Shading Based on the Shading Intensity
To use shading that is calculated from the shading intensity (accounting for the effect of shadows cast by light), set the reserved uniform dmp_Gas.colorLutInput
to GL_GAS_LIGHT_FACTOR_DMP
, using the glUniform1i
function. This selects the shading intensity as input to the shading lookup table.
The shading intensity is the total of two calculated values: the planar shading intensity (IG) and the view shading intensity (IS). IG and IS are defined as follows.
ig = r × (1.0 - lightAtt × d1)
IG = (1.0 - ig) × lightMin + ig × lightMax
is = LZ × (1.0 - scattAtt × d1)
IS = (1.0 - is) × scattMin + is × scattMax
d1 is the result of multiplying the density information that does not account for intersections with polygon objects (D1), by the reciprocal of the maximum density. This mechanism is similar to the one used for shading based on density information. r is the R component output from the texture combiner that is used as fog input. lightAtt, lightMin, and lightMax are coefficients of the planar shading intensity. LZ is the light direction along the z-axis in eye coordinates. scattAtt, scattMin, and scattMax are coefficients of the view shading intensity. These all take values between 0.0
and 1.0
.
The planar shading intensity is proportional to r and (1.0 – lightAtt × d1), as its equation shows. It approaches lightMax when r increases and lightMin when d1 increases. Likewise, the view shading intensity is proportional to LZ and (1.0 – scattAtt × d1). It approaches scattMax when LZ increases and scattMin when d1 increases.
Note that the shading intensity input to a shading lookup table is larger for gaseous bodies that are less dense. This emulates the real behavior of light, which penetrates through the thin (low-density) areas of a gaseous body, and is absorbed in the thick (high-density) areas. Accordingly, lightMin and scattMin represent the shading intensity when the effect of light is small, and lightMax and scattMax represent the shading intensity when the effect of light is large. lightAtt and scattAtt set the ratio of light attenuation caused by density. Note that, depending on the values set in the shading lookup tables, lightMin is not necessarily less than lightMax (and scattMin is not necessarily less than scattMax). Also note that alpha values are determined by fog coefficients whose input is proportional to density.
Coefficients for the planar shading intensity (lightMin, lightMax, and lightAtt) are set, as a group, in the reserved uniform dmp_Gas.lightXY
. Coefficients for the view shading intensity (scattMin, scattMax, and scattAtt), and the light direction along the z-axis in eye coordinates (LZ), are set as a group in the reserved uniform dmp_Gas.lightZ
. The minimum value, maximum value, and attenuation are set in that order. They are followed by LZ for the view shading intensity.
The following code shows how to set the shading intensity coefficients.
GLfloat lightXY[3], lightZ[4]; GLfloat lightMin, lightMax, lightAtt; GLfloat scattMin, scattMax, scattAtt, LZ; //... // lightXY lightXY[0] = lightMin; lightXY[1] = lightMax; lightXY[2] = lightAtt; // lightZ lightZ[0] = scattMin; lightZ[1] = scattMax; lightZ[2] = scattAtt; lightZ[3] = LZ; // Set Uniform glUniform3fv(glGetUniformLocation(progID, "dmp_Gas.lightXY"), 1, lightXY); glUniform4fv(glGetUniformLocation(progID, "dmp_Gas.lightZ"), 1, lightZ);
Alpha Shading
The results of shading the alpha component are determined by output from the lookup table for fog coefficients (dmp_Fog.sampler
), given the product of the gas attenuation (dmp_Gas.attenuation
) and the density information that accounts for intersections (D2) as input.
Fog coefficients are normally specified using a function that approaches 0.0
when the gas density is low, and 1.0
when the gas density is high.
// Fog Factor for(int i = 0; i < 128; i++) { fogTable[i]= 1.0f - exp(-8.0f * i / 128.0f); } for(int i = 0; i < 128; i++) { fogTable[128 + i] = fogTable[i + 1] - fogTable[i]; } fogTable[255] = 0; // Set LUT glGenTextures(1, &fogLUT_ID); glBindTexture(GL_LUT_TEXTURE0_DMP, fogLUT_ID); glTexImage1D(GL_LUT_TEXTURE0_DMP, 0, GL_LUMINANCEF_DMP, 256, 0, GL_LUMINANCEF_DMP, GL_FLOAT, fogTable);
The following sample code sets uniforms required by the shading pass and renders a quad (polygon), to which a gas texture has been applied.
// Bind Framebuffer(Colorbuffer) glBindFramebuffer(GL_FRAMEBUFFER, renderFboID); // Bind Gas Texture glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gasTexID); glUniform1i(glGetUniformLocation(progID, "dmp_Texture[0].samplerType"), GL_TEXTURE_2D); // Set TextureCombiner #5 glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[5].srcRgb"), GL_PREVIOUS, GL_PREVIOUS, GL_TEXTURE0); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[5].srcAlpha"), GL_PREVIOUS, GL_PREVIOUS, GL_TEXTURE0); // Set uniform for gas shading mode. glUniform1i(glGetUniformLocation(progID, "dmp_Fog.sampler"), 0); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.autoAcc"), GL_FALSE); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTR"), 1); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTG"), 2); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTB"), 3); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.shadingDensitySrc"), GL_GAS_DEPTH_DENSITY_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.colorLutInput"), GL_GAS_DENSITY_DMP); glUniform1f(glGetUniformLocation(progID, "dmp_Gas.accMax"), 1.0f/6.0f); glUniform4fv(glGetUniformLocation(progID, "dmp_Gas.lightZ"), 1, gasLightZ); glUniform3fv(glGetUniformLocation(progID, "dmp_Gas.lightXY"), 1, gasLightXY); // Change to gas shading mode. glUniform1i(glGetUniformLocation(progID, "dmp_FragOperation.mode"), GL_FRAGOP_MODE_GL_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_Fog.mode"), GL_GAS_DMP); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE);
// Gaseous Shading // Texture Coord float u0 = 0.0f; float v0 = 0.0f; float u1 = (GAS_ACC_WIDTH * 1.0f) / (GAS_TEX_WIDTH * 1.0f); float v1 = (GAS_ACC_HEIGHT * 1.0f) / (GAS_TEX_HEIGHT * 1.0f); GLfloat texCoord[8]= {u0, v0, u0, v1, u1, v1, u1, v0}; // Vertex GLushort quadIndex[6] = {0, 1, 2, 0, 2, 3}; GLfloat vertex[16] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f }; // Set Array GLfloat quadIndexID, quadVertexID, quadTexCoordID; glGenBuffers(1, &quadIndexID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIndexID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(GLushort), &quadIndex, GL_STATIC_DRAW); glGenBuffers(1, &quadVertexID); glBindBuffer(GL_ARRAY_BUFFER, quadVertexID); glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLfloat), &vertex, GL_STATIC_DRAW); glGenBuffers(1, &quadTexCoordID); glBindBuffer(GL_ARRAY_BUFFER, quadTexCoordID); glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), &texCoord, GL_STATIC_DRAW); // Draw Quad glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, quadVertexID); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, quadTexCoordID); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIndexID); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
12.6.4. Reserved Uniform
The following table lists the reserved uniforms used for gas.
Reserved Uniform |
Type |
Setting Value |
---|---|---|
|
|
Specifies the fragment operation mode. Specify gas mode in the density rendering pass. Specify standard mode in any other pass.
|
|
|
Specifies the mode for processing the fog pipeline. Specify gas mode in the shading pass. Disable fog in the density rendering pass. Specify |
|
|
Specifies the lookup table to use for fog coefficients. Fog coefficients are used to calculate alpha values for gaseous bodies. 0 to 31 |
|
|
Specifies the attenuation coefficient EZ in the depth direction. 10.0 (default) |
|
|
Specifies whether to automatically calculate the reciprocal of the maximum density value. Specify |
|
|
Specifies the reciprocal of the maximum density value. 0.0 or more 1.0 (default) |
|
|
Specifies the shading lookup table for each RGB component. 0 to 31 |
|
|
Specifies the density information to use for shading. Specify |
|
|
Specifies whether the density or shading intensity is given as input to the shading lookup tables. Specify |
|
|
Specifies factors used to control planar shading: the minimum intensity, maximum intensity, and density attenuation. (lightMin, lightMax, lightAtt) Each control value is between The default value is (0.0, 0.0, 0.0). |
|
|
Specifies factors used to control view shading: the minimum intensity, maximum intensity, density attenuation, and effect in the view direction. (scattMin, scattMax, scattAtt, LZ) Each control value is between The default value is (0.0, 0.0, 0.0, 0.0). |
|
|
Specifies the density attenuation coefficient to use when calculating alpha values for shading.
1.0 (default) |