// // TODO: // - implement texture1D, texture2D, texture3D, textureCube, // - implement shadow1D, shadow2D, // - implement dFdx, dFdy, // // // From Shader Spec, ver. 1.051 // // The output of the fragment shader goes on to be processed by the fixed function operations at // the back end of the OpenGL pipeline. Fragment shaders interface with the back end of the OpenGL // pipeline using the built-in variables gl_FragColor and gl_FragDepth, or by executing the discard // keyword. // // These variables may be written more than once within a fragment shader. If so, the last value // assigned is the one used in the subsequent fixed function pipeline. The values written to these // variables may be read back after writing them. Reading from these variables before writing them // results in an undefined value. The fixed functionality computed depth for a fragment may be // obtained by reading gl_FragCoord.z, described below. // // Writing to gl_FragColor specifies the fragment color that will be used by the subsequent fixed // functionality pipeline. If subsequent fixed functionality consumes fragment color and an // execution of a fragment shader does not write a value to gl_FragColor then the fragment color // consumed is undefined. // // If the frame buffer is configured as a color index buffer then behavior is undefined when using // a fragment shader. // // Writing to gl_FragDepth will establish the depth value for the fragment being processed. If // depth buffering is enabled, and a shader does not write gl_FragDepth, then the fixed function // value for depth will be used as the fragment’s depth value. If a shader statically assigns // a value to gl_FragDepth, and there is an execution path through the shader that does not set // gl_FragDepth, then the value of the fragment’s depth may be undefined for some executions of // the shader. That is, if a shader statically writes gl_FragDepth, then it is responsible for // always writing it. There is also no guarantee that a shader can compute the same depth value // as the fixed function value; an implementation will provide invariant results within shaders // computing depth with the same source-level expression, but invariance is not provided between // shaders and fixed functionality. // // Writes to gl_FragColor and gl_FragDepth need not be clamped within a shader. The fixed // functionality pipeline following the fragment shader will clamp these values. // // If a shader executes the discard keyword, the fragment is discarded, and the values of // gl_FragDepth and gl_FragColor become irrelevant. // // The variable gl_FragCoord is available as a read-only variable from within fragment shaders // and it holds the window relative coordinates x, y, z, and 1/w values for the fragment. This // value is the result of the fixed functionality that interpolates primitives after vertex // processing to generate fragments. The z component is the depth value that would be used for // the fragment’s depth if a shader contained no writes to gl_FragDepth. This is useful for // invariance if a shader conditionally computes gl_FragDepth but otherwise wants the fixed // functionality fragment depth. // // The fragment shader has access to the read-only built-in variable gl_FrontFacing whose value // is true if the fragment belongs to a front-facing primitive. One use of this is to emulate // two-sided lighting by selecting one of two colors calculated by the vertex shader. // // The built-in variables that are accessible from a fragment shader are intrinsically given types // as follows: // vec4 gl_FragCoord; bool gl_FrontFacing; vec4 gl_FragColor; float gl_FragDepth; // // However, they do not behave like variables with no qualifier; their behavior is as described // above. These built-in variables have global scope. // // // Unlike user-defined varying variables, the built-in varying variables don’t have a strict // one-to-one correspondence between the vertex language and the fragment language. Two sets are // provided, one for each language. Their relationship is described below. // // The following varying variables are available to read from in a fragment shader. The gl_Color // and gl_SecondaryColor names are the same names as attributes passed to the vertex shader. // However, there is no name conflict, because attributes are visible only in vertex shaders // and the following are only visible in a fragment shader. // varying vec4 gl_Color; varying vec4 gl_SecondaryColor; varying vec4 gl_TexCoord[]; // at most will be gl_MaxTextureCoordsARB varying float gl_FogFragCoord; // // The values in gl_Color and gl_SecondaryColor will be derived automatically by the system from // gl_FrontColor, gl_BackColor, gl_FrontSecondaryColor, and gl_BackSecondaryColor based on which // face is visible. If fixed functionality is used for vertex processing, then gl_FogFragCoord will // either be the z-coordinate of the fragment in eye space, or the interpolation of the fog // coordinate, as described in section 3.10 of the OpenGL 1.4 Specification. The gl_TexCoord[] // values are the interpolated gl_TexCoord[] values from a vertex shader or the texture coordinates // of any fixed pipeline based vertex functionality. // // Indices to the fragment shader gl_TexCoord array are as described above in the vertex shader // text. // // // The OpenGL Shading Language defines an assortment of built-in convenience functions for scalar // and vector operations. Many of these built-in functions can be used in more than one type // of shader, but some are intended to provide a direct mapping to hardware and so are available // only for a specific type of shader. // // The built-in functions basically fall into three categories: // // • They expose some necessary hardware functionality in a convenient way such as accessing // a texture map. There is no way in the language for these functions to be emulated by a shader. // // • They represent a trivial operation (clamp, mix, etc.) that is very simple for the user // to write, but they are very common and may have direct hardware support. It is a very hard // problem for the compiler to map expressions to complex assembler instructions. // // • They represent an operation graphics hardware is likely to accelerate at some point. The // trigonometry functions fall into this category. // // Many of the functions are similar to the same named ones in common C libraries, but they support // vector input as well as the more traditional scalar input. // // Applications should be encouraged to use the built-in functions rather than do the equivalent // computations in their own shader code since the built-in functions are assumed to be optimal // (e.g., perhaps supported directly in hardware). // // User code can replace built-in functions with their own if they choose, by simply re-declaring // and defining the same name and argument list. // // // Texture Lookup Functions // // Texture lookup functions are available to both vertex and fragment shaders. However, level // of detail is not computed by fixed functionality for vertex shaders, so there are some // differences in operation between vertex and fragment texture lookups. The functions in the table // below provide access to textures through samplers, as set up through the OpenGL API. Texture // properties such as size, pixel format, number of dimensions, filtering method, number of mip-map // levels, depth comparison, and so on are also defined by OpenGL API calls. Such properties are // taken into account as the texture is accessed via the built-in functions defined below. // // If a non-shadow texture call is made to a sampler whose texture has depth comparisons enabled, // then results are undefined. If a shadow texture call is made to a sampler whose texture does not // have depth comparisions enabled, the results are also undefined. // // In all functions below, the bias parameter is optional for fragment shaders. The bias parameter // is not accepted in a vertex shader. For a fragment shader, if bias is present, it is added to // the calculated level of detail prior to performing the texture access operation. If the bias // parameter is not provided, then the implementation automatically selects level of detail: // For a texture that is not mip-mapped, the texture is used directly. If it is mip-mapped and // running in a fragment shader, the LOD computed by the implementation is used to do the texture // lookup. If it is mip-mapped and running on the vertex shader, then the base texture is used. // // The built-ins suffixed with “Lod” are allowed only in a vertex shader. For the “Lod” functions, // lod is directly used as the level of detail. // // // Use the texture coordinate coord to do a texture lookup in the 1D texture currently bound // to sampler. For the projective (“Proj”) versions, the texture coordinate coord.s is divided by // the last component of coord. // // XXX vec4 texture1D (sampler1D sampler, float coord, float bias) { return vec4 (0.0); } vec4 texture1DProj (sampler1D sampler, vec2 coord, float bias) { return texture1D (sampler, coord.s / coord.t, bias); } vec4 texture1DProj (sampler1D sampler, vec4 coord, float bias) { return texture1D (sampler, coord.s / coord.q, bias); } // // Use the texture coordinate coord to do a texture lookup in the 2D texture currently bound // to sampler. For the projective (“Proj”) versions, the texture coordinate (coord.s, coord.t) is // divided by the last component of coord. The third component of coord is ignored for the vec4 // coord variant. // // XXX vec4 texture2D (sampler2D sampler, vec2 coord, float bias) { return vec4 (0.0); } vec4 texture2DProj (sampler2D sampler, vec3 coord, float bias) { return texture2D (sampler, vec2 (coord.s / coord.p, coord.t / coord.p), bias); } vec4 texture2DProj (sampler2D sampler, vec4 coord, float bias) { return texture2D (sampler, vec2 (coord.s / coord.q, coord.s / coord.q), bias); } // // Use the texture coordinate coord to do a texture lookup in the 3D texture currently bound // to sampler. For the projective (“Proj”) versions, the texture coordinate is divided by coord.q. // // XXX vec4 texture3D (sampler3D sampler, vec3 coord, float bias) { return vec4 (0.0); } vec4 texture3DProj (sampler3D sampler, vec4 coord, float bias) { return texture3DProj (sampler, vec3 (coord.s / coord.q, coord.t / coord.q, coord.p / coord.q), bias); } // // Use the texture coordinate coord to do a texture lookup in the cube map texture currently bound // to sampler. The direction of coord is used to select which face to do a 2-dimensional texture // lookup in, as described in section 3.8.6 in version 1.4 of the OpenGL specification. // // XXX vec4 textureCube (samplerCube sampler, vec3 coord, float bias) { return vec4 (0.0); } // // Use texture coordinate coord to do a depth comparison lookup on the depth texture bound // to sampler, as described in section 3.8.14 of version 1.4 of the OpenGL specification. The 3rd // component of coord (coord.p) is used as the R value. The texture bound to sampler must be a // depth texture, or results are undefined. For the projective (“Proj”) version of each built-in, // the texture coordinate is divide by coord.q, giving a depth value R of coord.p/coord.q. The // second component of coord is ignored for the “1D” variants. // // XXX vec4 shadow1D (sampler1DShadow sampler, vec3 coord, float bias) { return vec4 (0.0); } // XXX vec4 shadow2D (sampler2DShadow sampler, vec3 coord, float bias) { return vec4 (0.0); } vec4 shadow1DProj (sampler1DShadow sampler, vec4 coord, float bias) { return shadow1D (sampler, vec3 (coord.s / coord.q, 0.0, coord.p / coord.q), bias); } vec4 shadow2DProj (sampler2DShadow sampler, vec4 coord, float bias) { return shadow2D (sampler, vec3 (coord.s / coord.q, coord.t / coord.q, coord.p / coord.q), bias); } // // Fragment processing functions are only available in shaders intended for use on the fragment // processor. Derivatives may be computationally expensive and/or numerically unstable. Therefore, // an OpenGL implementation may approximate the true derivatives by using a fast but not entirely // accurate derivative computation. // // The expected behavior of a derivative is specified using forward/backward differencing. // // Forward differencing: // // F(x+dx) - F(x) ~ dFdx(x) * dx 1a // dFdx(x) ~ (F(x+dx) - F(x)) / dx 1b // // Backward differencing: // // F(x-dx) - F(x) ~ -dFdx(x) * dx 2a // dFdx(x) ~ (F(x) - F(x-dx)) / dx 2b // // With single-sample rasterization, dx <= 1.0 in equations 1b and 2b. For multi-sample // rasterization, dx < 2.0 in equations 1b and 2b. // // dFdy is approximated similarly, with y replacing x. // // A GL implementation may use the above or other methods to perform the calculation, subject // to the following conditions: // // 1) The method may use piecewise linear approximations. Such linear approximations imply that // higher order derivatives, dFdx(dFdx(x)) and above, are undefined. // // 2) The method may assume that the function evaluated is continuous. Therefore derivatives within // the body of a non-uniform conditional are undefined. // // 3) The method may differ per fragment, subject to the constraint that the method may vary by // window coordinates, not screen coordinates. The invariance requirement described in section // 3.1 of the OpenGL 1.4 specification is relaxed for derivative calculations, because // the method may be a function of fragment location. // // Other properties that are desirable, but not required, are: // // 4) Functions should be evaluated within the interior of a primitive (interpolated, not // extrapolated). // // 5) Functions for dFdx should be evaluated while holding y constant. Functions for dFdy should // be evaluated while holding x constant. However, mixed higher order derivatives, like // dFdx(dFdy(y)) and dFdy(dFdx(x)) are undefined. // // In some implementations, varying degrees of derivative accuracy may be obtained by providing // GL hints (section 5.6 of the OpenGL 1.4 specification), allowing a user to make an image // quality versus speed tradeoff. // // // Returns the derivative in x using local differencing for the input argument p. // // XXX float dFdx (float p) { return 0.0; } // XXX vec2 dFdx (vec2 p) { return vec2 (0.0); } // XXX vec3 dFdx (vec3 p) { return vec3 (0.0); } // XXX vec4 dFdx (vec4 p) { return vec4 (0.0); } // // Returns the derivative in y using local differencing for the input argument p. // // These two functions are commonly used to estimate the filter width used to anti-alias procedural // textures.We are assuming that the expression is being evaluated in parallel on a SIMD array so // that at any given point in time the value of the function is known at the grid points // represented by the SIMD array. Local differencing between SIMD array elements can therefore // be used to derive dFdx, dFdy, etc. // // XXX float dFdy (float p) { return 0.0; } // XXX vec2 dFdy (vec2 p) { return vec2 (0.0); } // XXX vec3 dFdy (vec3 p) { return vec3 (0.0); } // XXX vec4 dFdy (vec4 p) { return vec4 (0.0); } // // Returns the sum of the absolute derivative in x and y using local differencing for the input // argument p, i.e.: // // return = abs (dFdx (p)) + abs (dFdy (p)); // float fwidth (float p) { return abs (dFdx (p)) + abs (dFdy (p)); } vec2 fwidth (vec2 p) { return abs (dFdx (p)) + abs (dFdy (p)); } vec3 fwidth (vec3 p) { return abs (dFdx (p)) + abs (dFdy (p)); } vec4 fwidth (vec4 p) { return abs (dFdx (p)) + abs (dFdy (p)); }