1 // regcombine.cpp
2 //
3 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
4 //
5 // Some functions for setting up the nVidia register combiners
6 // extension for pretty rendering effects.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 
13 #include "gl.h"
14 #include "glext.h"
15 #include "regcombine.h"
16 
17 #if 0
18 namespace rc
19 {
20     enum {
21         Combiner0 = GL_COMBINER0_NV,
22         Combiner1 = GL_COMBINER1_NV,
23         Combiner2 = GL_COMBINER2_NV,
24         Combiner3 = GL_COMBINER3_NV,
25     };
26 
27     enum {
28         A  = GL_VARIABLE_A_NV,
29         B  = GL_VARIABLE_B_NV,
30         C  = GL_VARIABLE_C_NV,
31         D  = GL_VARIABLE_D_NV,
32         E  = GL_VARIABLE_E_NV,
33         F  = GL_VARIABLE_F_NV,
34         G  = GL_VARIABLE_G_NV,
35     };
36 
37     enum {
38         RGBPortion   = GL_RGB,
39         AlphaPortion = GL_ALPHA,
40         BluePortion  = GL_BLUE,
41     };
42 
43     enum {
44         UnsignedIdentity    = GL_UNSIGNED_IDENTITY_NV,
45         UnsignedInvert      = GL_UNSIGNED_INVERT_NV,
46         ExpandNormal        = GL_EXPAND_NORMAL_NV,
47     };
48 };
49 #endif
50 
51 namespace rc
52 {
53     void parameter(GLenum, Color);
54 };
55 
56 
parameter(GLenum which,Color color)57 void rc::parameter(GLenum which, Color color)
58 {
59     float f[4];
60     f[0] = color.red();
61     f[1] = color.green();
62     f[2] = color.blue();
63     f[3] = color.alpha();
64     glx::glCombinerParameterfvNV(which, f);
65 }
66 
SetupCombinersBumpMap(Texture & bumpTexture,Texture & normalizationTexture,Color ambientColor)67 void SetupCombinersBumpMap(Texture& bumpTexture,
68                            Texture& normalizationTexture,
69                            Color ambientColor)
70 {
71     glEnable(GL_REGISTER_COMBINERS_NV);
72 
73     glDisable(GL_LIGHTING);
74     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
75     glEnable(GL_TEXTURE_CUBE_MAP_ARB);
76     normalizationTexture.bind();
77 
78     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
79     glEnable(GL_TEXTURE_2D);
80     bumpTexture.bind();
81 
82     // Just a single combiner stage required . . .
83     glx::glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);
84 
85     float ambient[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
86     ambient[0] = ambientColor.red();
87     ambient[1] = ambientColor.green();
88     ambient[2] = ambientColor.blue();
89     glx::glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, ambient);
90 
91     // Compute N dot L in the RGB portion of combiner 0
92     // Load register A with a normal N from the normal map
93     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB,
94                            GL_VARIABLE_A_NV, GL_TEXTURE0_ARB,
95                            GL_EXPAND_NORMAL_NV, GL_RGB);
96 
97     // Load register B with the normalized light direction L
98     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB,
99                            GL_VARIABLE_B_NV, GL_TEXTURE1_ARB,
100                            GL_EXPAND_NORMAL_NV, GL_RGB);
101 
102     // Compute N dot L
103     glx::glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB,
104                             GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
105                             GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE);
106 
107     // Compute the self-shadowing term in the alpha portion of combiner 0
108     // A = 1
109     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_A_NV,
110                            GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
111     // B = L.z
112     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_B_NV,
113                            GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_BLUE);
114     // C = 1
115     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_C_NV,
116                            GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
117     // D = L.z
118     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_D_NV,
119                            GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_BLUE);
120 
121     // Create a steep ramp function for self-shadowing
122     // SPARE0 = 4*(A*B+C*D) = 4*(1*L.z + 1*L.z) = 8 * L.z
123     glx::glCombinerOutputNV(GL_COMBINER0_NV, GL_ALPHA,
124                             GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV,
125                             GL_SCALE_BY_FOUR_NV, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
126 
127     // A = SPARE0_alpha = per-pixel self-shadowing term
128     glx::glFinalCombinerInputNV(GL_VARIABLE_A_NV,
129                                 GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
130     glx::glFinalCombinerInputNV(GL_VARIABLE_B_NV,
131                                 GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
132     // C = zero
133     glx::glFinalCombinerInputNV(GL_VARIABLE_C_NV,
134                                 GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
135     // D = ambient color
136     glx::glFinalCombinerInputNV(GL_VARIABLE_D_NV,
137                                 GL_CONSTANT_COLOR0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
138     // G = diffuse illumination contribution = L dot N
139     glx::glFinalCombinerInputNV(GL_VARIABLE_G_NV,
140                                 GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
141 }
142 
143 
144 // Set up register combiners for per-pixel diffuse lighting, with a base
145 // texture, ambient color, material color, and normal cube map.  We could use
146 // just a plain old color cube map, but we use a normal map instead for
147 // consistency with bump mapped surfaces.  Only one pass with a single
148 // combiner is required.
SetupCombinersSmooth(Texture & baseTexture,Texture & normalizationTexture,Color ambientColor,bool invert)149 void SetupCombinersSmooth(Texture& baseTexture,
150                           Texture& normalizationTexture,
151                           Color ambientColor,
152                           bool invert)
153 {
154     glEnable(GL_REGISTER_COMBINERS_NV);
155 
156     glDisable(GL_LIGHTING);
157     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
158     glEnable(GL_TEXTURE_CUBE_MAP_ARB);
159     normalizationTexture.bind();
160     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
161     glEnable(GL_TEXTURE_2D);
162     baseTexture.bind();
163 
164     // Just a single combiner stage required . . .
165     glx::glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);
166 
167     float ambient[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
168     ambient[0] = ambientColor.red();
169     ambient[1] = ambientColor.green();
170     ambient[2] = ambientColor.blue();
171     glx::glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, ambient);
172 
173     // A = primary color
174     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
175                            GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV,
176                            GL_RGB);
177     // B = base texture color
178     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
179                            GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
180     // SPARE1_rgb = primary * texture
181     glx::glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB,
182                             GL_SPARE1_NV, GL_DISCARD_NV, GL_DISCARD_NV,
183                             GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
184 
185     // A = 1
186     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_A_NV,
187                            GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
188     // B = L.z
189     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_B_NV,
190                            GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV,
191                            GL_BLUE);
192     // SPARE0_alpha = 1 * L.z
193     glx::glCombinerOutputNV(GL_COMBINER0_NV, GL_ALPHA,
194                             GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
195                             GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
196 
197     // E = SPARE1_rgb = base texture color * primary
198     glx::glFinalCombinerInputNV(GL_VARIABLE_E_NV,
199                                 GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
200     // F = ambient color
201     glx::glFinalCombinerInputNV(GL_VARIABLE_F_NV,
202                                 GL_CONSTANT_COLOR0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
203     // A = SPARE1_rgb = base texture color * primary
204     glx::glFinalCombinerInputNV(GL_VARIABLE_A_NV,
205                                 GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
206     // B = SPARE0_alpha = L.z
207     glx::glFinalCombinerInputNV(GL_VARIABLE_B_NV,
208                                 GL_SPARE0_NV,
209                                 invert ? GL_UNSIGNED_INVERT_NV : GL_UNSIGNED_IDENTITY_NV,
210                                 GL_ALPHA);
211     // C = zero
212     glx::glFinalCombinerInputNV(GL_VARIABLE_C_NV,
213                                 GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
214     // D = SPARE1_rgb = E*F = texture * primary * ambient color
215     glx::glFinalCombinerInputNV(GL_VARIABLE_D_NV,
216                                 GL_E_TIMES_F_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
217     // G = 1
218     glx::glFinalCombinerInputNV(GL_VARIABLE_G_NV,
219                                 GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
220 
221 }
222 
223 
224 // Normal map should be bound as texture 1 and the base map should be bound
225 // as texture 0.
SetupCombinersDecalAndBumpMap(Texture &,Color ambientColor,Color diffuseColor)226 void SetupCombinersDecalAndBumpMap(Texture& /*bumpTexture*/,
227                                    Color ambientColor,
228                                    Color diffuseColor)
229 {
230     glEnable(GL_REGISTER_COMBINERS_NV);
231     glx::glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 2);
232 
233     rc::parameter(GL_CONSTANT_COLOR0_NV, ambientColor);
234     rc::parameter(GL_CONSTANT_COLOR1_NV, diffuseColor);
235 
236     // Compute N dot L in the RGB portion of combiner 0
237     // Load register A with a normal N from the bump map
238     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB,
239                            GL_VARIABLE_A_NV, GL_TEXTURE1_ARB,
240                            GL_EXPAND_NORMAL_NV, GL_RGB);
241 
242     // Load register B with the primary color, which contains the surface
243     // space light direction L.  Because the color is linearly interpolated
244     // across triangles, the direction may become denormalized; however, in
245     // Celestia, planet surfaces are tessellated finely enough that this
246     // is not a problem.
247     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB,
248                            GL_VARIABLE_B_NV, GL_PRIMARY_COLOR_NV,
249                            GL_EXPAND_NORMAL_NV, GL_RGB);
250 
251     // Product C*D computes diffuse color * texture
252     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB,
253                            GL_VARIABLE_C_NV, GL_TEXTURE0_ARB,
254                            GL_UNSIGNED_IDENTITY_NV, GL_RGB);
255     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB,
256                            GL_VARIABLE_D_NV, GL_CONSTANT_COLOR1_NV,
257                            GL_UNSIGNED_IDENTITY_NV, GL_RGB);
258 
259     // Compute N dot L in spare0 and diffuse * decal texture in spare1
260     glx::glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB,
261                             GL_SPARE0_NV, GL_SPARE1_NV, GL_DISCARD_NV,
262                             GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE);
263 
264     // Compute the self-shadowing term in the alpha portion of combiner 0
265     // A = 1
266     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_A_NV,
267                            GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
268     // B = L.z
269     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_B_NV,
270                            GL_PRIMARY_COLOR_NV, GL_EXPAND_NORMAL_NV, GL_BLUE);
271     // C = 1
272     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_C_NV,
273                            GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
274     // D = L.z
275     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_D_NV,
276                            GL_PRIMARY_COLOR_NV, GL_EXPAND_NORMAL_NV, GL_BLUE);
277 
278     // Create a steep ramp function for self-shadowing
279     // SPARE0 = 4*(A*B+C*D) = 4*(1*L.z + 1*L.z) = 8 * L.z
280     glx::glCombinerOutputNV(GL_COMBINER0_NV, GL_ALPHA,
281                             GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV,
282                             GL_SCALE_BY_FOUR_NV, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
283 
284     // In the second combiner, sum the ambient color and product of the
285     // diffuse and self-shadowing terms.
286     glx::glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV,
287                            GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
288     glx::glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV,
289                            GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
290     glx::glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV,
291                            GL_CONSTANT_COLOR0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
292     glx::glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV,
293                            GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
294     glx::glCombinerOutputNV(GL_COMBINER1_NV, GL_RGB,
295                             GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV,
296                             GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
297 
298     // E = SPARE0 = fragment brightness, including ambient, diffuse, and
299     // self shadowing.
300     glx::glFinalCombinerInputNV(GL_VARIABLE_E_NV,
301                                 GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
302     // F = spare1 = decal texture rgb * diffuse color
303     glx::glFinalCombinerInputNV(GL_VARIABLE_F_NV,
304                                 GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
305 
306     // A = fog factor
307     glx::glFinalCombinerInputNV(GL_VARIABLE_A_NV,
308                                 GL_FOG, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
309     // B = color
310     glx::glFinalCombinerInputNV(GL_VARIABLE_B_NV,
311                                 GL_E_TIMES_F_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
312     // C = fog color
313     glx::glFinalCombinerInputNV(GL_VARIABLE_C_NV,
314                                 GL_FOG, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
315     // D = zero
316     glx::glFinalCombinerInputNV(GL_VARIABLE_D_NV,
317                                 GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
318 
319     // G = diffuse illumination contribution = L dot N
320     glx::glFinalCombinerInputNV(GL_VARIABLE_G_NV,
321                                 GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
322 }
323 
324 
325 // Set up the combiners to a texture with gloss map in the alpha channel.
SetupCombinersGlossMap(int glossMap)326 void SetupCombinersGlossMap(int glossMap)
327 {
328     glEnable(GL_REGISTER_COMBINERS_NV);
329 
330     // Just a single combiner stage required . . .
331     glx::glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);
332 
333     // A = primary color
334     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
335                            GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
336     // B = base texture color
337     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
338                            GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
339     // C = secondary color
340     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV,
341                            GL_SECONDARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
342     if (glossMap != 0)
343     {
344         // D = texture1 rgb (gloss mask)
345         glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV,
346                                glossMap, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
347     }
348     else
349     {
350         // D = texture alpha (gloss mask)
351         glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV,
352                                GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
353     }
354 
355     // SPARE0_rgb = primary * texture.rgb + secondary * texture.alpha
356     glx::glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB,
357                             GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV,
358                             GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
359 
360     // A = SPARE0_rgb
361     glx::glFinalCombinerInputNV(GL_VARIABLE_A_NV,
362                               GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
363     // B = 1
364     glx::glFinalCombinerInputNV(GL_VARIABLE_B_NV,
365                                 GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
366     // C = zero
367     glx::glFinalCombinerInputNV(GL_VARIABLE_C_NV,
368                                 GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
369     // D = zero
370     glx::glFinalCombinerInputNV(GL_VARIABLE_D_NV,
371                                 GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
372     // G = 1
373     glx::glFinalCombinerInputNV(GL_VARIABLE_G_NV,
374                                 GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
375 }
376 
377 
378 // Set up the combiners to a texture with gloss in the alpha channel.
SetupCombinersGlossMapWithFog(int glossMap)379 void SetupCombinersGlossMapWithFog(int glossMap)
380 {
381     glEnable(GL_REGISTER_COMBINERS_NV);
382 
383     // Just a single combiner stage required . . .
384     glx::glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);
385 
386     // A = primary color
387     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
388                            GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
389     // B = base texture color
390     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
391                            GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
392     // C = secondary color
393     glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV,
394                            GL_SECONDARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
395     if (glossMap != 0)
396     {
397         // D = texture1 rgb (gloss mask)
398         glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV,
399                                glossMap, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
400     }
401     else
402     {
403         // D = texture alpha (gloss mask)
404         glx::glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV,
405                                GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
406     }
407 
408     // SPARE0_rgb = primary * texture.rgb + secondary * texture.alpha
409     glx::glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB,
410                             GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV,
411                             GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
412 
413     // A = fog factor
414     glx::glFinalCombinerInputNV(GL_VARIABLE_A_NV,
415                                 GL_FOG, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
416     // B = spare0_rgb
417     glx::glFinalCombinerInputNV(GL_VARIABLE_B_NV,
418                                 GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
419     // C = fog color
420     glx::glFinalCombinerInputNV(GL_VARIABLE_C_NV,
421                                 GL_FOG, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
422     // D = zero
423     glx::glFinalCombinerInputNV(GL_VARIABLE_D_NV,
424                                 GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
425     // G = 1
426     glx::glFinalCombinerInputNV(GL_VARIABLE_G_NV,
427                                 GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
428 }
429 
430 
DisableCombiners()431 void DisableCombiners()
432 {
433     glDisable(GL_REGISTER_COMBINERS_NV);
434     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
435     glDisable(GL_TEXTURE_CUBE_MAP_ARB);
436     glDisable(GL_TEXTURE_2D);
437     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
438 }
439