/*---------------------------------------------------------------------------* Project: Horizon File: Gas.cpp Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 46365 $ *---------------------------------------------------------------------------*/ /* *------------------------------------------------------------ * Copyright(c) 2009-2010 by Digital Media Professionals Inc. * All rights reserved. *------------------------------------------------------------ * This source code is the confidential and proprietary * of Digital Media Professionals Inc. *------------------------------------------------------------ */ #include #include "Gas.h" #include "Util.h" #include extern GLuint pAccId; extern GLuint pPostId; extern struct gas_data gas; /* * Local function declaration */ /* * Local definition */ #define SHADING_WIDTH GAS_TEX_WIDTH #define SHADING_HEIGHT GAS_TEX_HEIGHT /* buffer id */ static struct tagBufID { GLuint acc; /* frame buffer for accumulation */ GLuint shading; /* frame buffer for shading */ GLuint accZ; /* Z buffer for accumulation */ } gasbuf; /* texture id */ GLuint gasacctex; /* gas texture to accumulation */ GLuint gastex; /* gas texture for shading pass */ /* particle pattern file names */ static char* particle_files[PARTICLE_PATTERNS] = {PARTICLE_FILES}; /*=======================================================*/ /* buffer initialization */ /*=======================================================*/ void GasInitialize(void) { /* * Generate framebuffers and texture for gas rendering */ /* generation */ glGenFramebuffers(1, (GLuint*)&gasbuf.acc); glGenFramebuffers(1, (GLuint*)&gasbuf.shading); glGenTextures(1, (GLuint*)&gasacctex); glGenTextures(1, (GLuint*)&gastex); glGenRenderbuffers(1, (GLuint*)&gasbuf.accZ); /* * Initialize accumulation buffer (destination of gas accumulation rendering) * (this buffer is reused as a destination of shading) */ /* initialize gas accumulation texture */ glBindTexture(GL_TEXTURE_2D, gasacctex); glTexImage2D(GL_TEXTURE_2D, 0, GL_GAS_DMP, GAS_ACC_WIDTH, GAS_ACC_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); /* Attach texture to framebuffer */ glBindFramebuffer(GL_FRAMEBUFFER, gasbuf.acc); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gasacctex, 0); /* default z buffer is defferent size from this buffer, so it is * necessary to prepare same size z buffer. */ glBindRenderbuffer(GL_RENDERBUFFER, gasbuf.accZ) ; glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, GAS_ACC_WIDTH, GAS_ACC_HEIGHT); glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gasbuf.accZ); /* * Initialize accumulation pow2 texture area (copy destination of accumulation result or shading result) */ glBindTexture(GL_TEXTURE_2D, gastex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GAS_TEX_WIDTH, GAS_TEX_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glBindFramebuffer(GL_FRAMEBUFFER, gasbuf.shading); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gastex, 0); } /*=======================================================*/ /* Setup of default gasesous object data structure */ /*=======================================================*/ void DefaultGasObject(struct gas_data *gas, float *gasColorTable) { gas->_dela_z = 200.0f; gas->_autoAcc = GL_FALSE; gas->_densMax = 1.0f; gas->_lightDirX = 0.0f; gas->_lightDirY = 0.0f; gas->_LightXY[0] = 0.0f; gas->_LightXY[1] = 0.0f; gas->_LightXY[2] = 0.0f; gas->_LightXY[3] = 0.0f; /* unsused */ gas->_LightZ[0] = 1.0f; gas->_LightZ[1] = 0.0f; gas->_LightZ[2] = 1.0f; gas->_LightZ[3] = 1.0f; gas->shadingDensitySrc = GL_GAS_PLAIN_DENSITY_DMP; gas->colorLutInput = GL_GAS_DENSITY_DMP; float dxt = 1.0f / 127.0f; float xt = 0; for (int i = 0; i < 128; i++) { gas->fogTable[i]= 1.0f - exp(-15.0f * xt); xt += dxt; } for (int i = 0; i < 128; i++) { gas->fogTable[128 + i] = gas->fogTable[i + 1] - gas->fogTable[i]; } gas->fogTable[255] = 0; glGenTextures(1, &gas->CollectionLUT_ID); glGenTextures(1, &gas->FogLut_ID); glBindTexture(GL_TEXTURE_COLLECTION_DMP, gas->CollectionLUT_ID); glBindTexture(GL_LUT_TEXTURE0_DMP, gas->FogLut_ID); glTexImage1D(GL_LUT_TEXTURE0_DMP, 0, GL_LUMINANCEF_DMP, 256, 0, GL_LUMINANCEF_DMP, GL_FLOAT, gas->fogTable); glGenTextures(3,&gas->gasTransfert_ID[0]); for(int i = 0; i < 8; i++) { gas->RR[i] = gasColorTable[3 * i + 0]; gas->GG[i] = gasColorTable[3 * i + 1]; gas->BB[i] = gasColorTable[3 * i + 2]; gas->RR[8 + i] = gasColorTable[3 * (i + 1) + 0] - gasColorTable[3 * i + 0]; gas->GG[8 + i] = gasColorTable[3 * (i + 1) + 1] - gasColorTable[3 * i + 1]; gas->BB[8 + i] = gasColorTable[3 * (i + 1) + 2] - gasColorTable[3 * i + 2]; } gas->RR[15] = 0.0f; gas->GG[15] = 0.0f; gas->BB[15] = 0.0f; glBindTexture(GL_LUT_TEXTURE1_DMP, gas->gasTransfert_ID[0]); glTexImage1D(GL_LUT_TEXTURE1_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, gas->RR); glBindTexture(GL_LUT_TEXTURE2_DMP, gas->gasTransfert_ID[1]); glTexImage1D(GL_LUT_TEXTURE2_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, gas->GG); glBindTexture(GL_LUT_TEXTURE3_DMP, gas->gasTransfert_ID[2]); glTexImage1D(GL_LUT_TEXTURE3_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, gas->BB); 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 tex_unit[8]= {u0, v0, u0, v1, u1, v1, u1, v0}; GLfloat LX0, LY0, LX1, LY1; LX0 = 0.0f; LY0 = 0.0f; LX1 = gas->_lightDirX; LY1 = gas->_lightDirY; GLfloat vertex_color[16] = { LX0, 0.0f, 0.0f, LY0, LX0, 0.0f, 0.0f, LY1, LX1, 0.0f, 0.0f, LY1, LX1, 0.0f, 0.0f, LY0 }; GLushort _quadIndex[6] = {0, 1, 2, 0, 2, 3}; GLfloat vertex_unit[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 }; glGenBuffers(1, &gas->quad_index_ID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gas->quad_index_ID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6*sizeof(GLushort), &_quadIndex, GL_STATIC_DRAW); glGenBuffers(1, &gas->quad_vertBuf_ID); glBindBuffer(GL_ARRAY_BUFFER,gas->quad_vertBuf_ID); glBufferData(GL_ARRAY_BUFFER, 16*sizeof(GLfloat), &vertex_unit, GL_STATIC_DRAW); glGenBuffers(1, &gas->quad_texBuf_ID); glBindBuffer(GL_ARRAY_BUFFER,gas->quad_texBuf_ID); glBufferData(GL_ARRAY_BUFFER, 8*sizeof(GLfloat), &tex_unit, GL_STATIC_DRAW); glGenBuffers(1, &gas->quad_colBuf_ID); glBindBuffer(GL_ARRAY_BUFFER,gas->quad_colBuf_ID); glBufferData(GL_ARRAY_BUFFER, 16*sizeof(GLfloat), &vertex_color, GL_STATIC_DRAW); /* * Load particle patterns */ glGenTextures(PARTICLE_PATTERNS, &gas->pattern[0]); for (int i = 0; i < PARTICLE_PATTERNS; i++) { bool alpha; glBindTexture(GL_TEXTURE_2D, gas->pattern[i]); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); loadTexture(particle_files[i], GL_TEXTURE_2D, 0, alpha); } } /*=======================================================*/ /* accumulation pass */ /*=======================================================*/ void GasAccumulation() { /* * In accumulation pass, particles are accumulated into gas accumulation buffer as * density information. This buffer is 'gasbuf.acc' in this sample. */ /* Bind accumulation buffer as a rendering target */ glBindFramebuffer(GL_FRAMEBUFFER, gasbuf.acc); /* Set viewport */ glViewport(0, 0, GAS_ACC_WIDTH, GAS_ACC_HEIGHT); /* Clear the buffer content (only color!) */ glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); /* use gas accumulation related program */ glUseProgram(pAccId); /* misc state */ glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glDisable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); /* texture pattern binding */ /* assuming only 1 pattern */ glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gas.pattern[0]); /* setup uniforms */ /* note that the program is shared with standard geometry to uniforms need to be reapply for each drawing call */ glUniform1i(glGetUniformLocation(pAccId, "dmp_Texture[0].samplerType"), GL_TEXTURE_2D); glUniform1i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].combineRgb"), GL_MODULATE); glUniform1i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].srcRgb"), GL_TEXTURE0, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); glUniform3i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].srcAlpha"), GL_TEXTURE0, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); /* Set the mode for the per fragment operation (gas accumulation mode) */ glUniform1i(glGetUniformLocation(pAccId, "dmp_FragOperation.mode"), GL_FRAGOP_MODE_GAS_ACC_DMP); /* Set this value to control the accuracy of z intersection of surface and gaseous objects */ glUniform1f(glGetUniformLocation(pAccId, "dmp_Gas.deltaZ"),gas._dela_z); glUniform1i(glGetUniformLocation(pAccId, "dmp_Fog.mode"), GL_FALSE); /* * Setup buffer */ glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, gas.gasgeo_center_ID); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, gas.gasgeo_tx0_ID); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, gas.gasgeo_density_ID); glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gas.gasgeo_tri_ID); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); glFinish(); } /*=======================================================*/ /* shading pass */ /*=======================================================*/ void GasShading(void) { /* * In shading pass, shaded gaseous image is blended to DISPLAY_BUFFER. * Accumulated density information is used as a gas texture. * Note that DISPLAY_BUFFER is defferent size * from gas texture and PICA does not support filter for gas format texture. * So gaseous shaded image is rendered to another buffer and then blend to * DIPLAY_BUFFER. */ /* Bind shading buffer and set viewport */ glBindFramebuffer(GL_FRAMEBUFFER, gasbuf.shading); glViewport(0, 0, SHADING_WIDTH, SHADING_HEIGHT); /* use gas accumulation related program */ glUseProgram(pPostId); /* Bind gas texture (accumulation result) */ glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gasacctex); /* Setup blending unit #0 * r component of primary color has the influence of LIGHT_X and LIGHT_Y * this output is transfered to FOG unit when FOG_MODE is set to GAS_DMP */ glUniform1i(glGetUniformLocation(pPostId, "dmp_Texture[0].samplerType"), GL_TEXTURE_2D); glUniform1i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].combineRgb"), GL_ADD); glUniform1i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].combineAlpha"), GL_ADD); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_ALPHA, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].srcRgb"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].srcAlpha"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); /* Setup blending unit #5 * This is HW requirement. See specification document for more details */ glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[5].srcRgb"), GL_PREVIOUS, GL_PREVIOUS, GL_TEXTURE0); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[5].srcAlpha"), GL_PREVIOUS, GL_PREVIOUS, GL_TEXTURE0); /* setup of gas shading */ glUniform1i(glGetUniformLocation(pPostId, "dmp_Fog.sampler"), 0); glUniform1i(glGetUniformLocation(pPostId, "dmp_Fog.mode"), GL_GAS_DMP); glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.autoAcc"), gas._autoAcc); glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.samplerTR"), 1); glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.samplerTG"), 2); glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.samplerTB"), 3); glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.shadingDensitySrc"), gas.shadingDensitySrc); glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.colorLutInput"), gas.colorLutInput); glUniform1f(glGetUniformLocation(pPostId, "dmp_Gas.accMax"), gas._densMax); glUniform4fv(glGetUniformLocation(pPostId, "dmp_Gas.lightZ"), 1, gas._LightZ); glUniform3fv(glGetUniformLocation(pPostId, "dmp_Gas.lightXY"), 1, gas._LightXY); glUniform1i(glGetUniformLocation(pPostId, "dmp_FragOperation.mode"), GL_FRAGOP_MODE_GL_DMP); /* * misc settings */ /* In this configuration shading result is once rendered * without blending. */ glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDepthMask(0); glBindTexture(GL_TEXTURE_COLLECTION_DMP, gas.CollectionLUT_ID); glBindTexture(GL_TEXTURE_2D, gasacctex); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, gas.quad_vertBuf_ID); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, gas.quad_texBuf_ID); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, gas.quad_colBuf_ID); glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gas.quad_index_ID); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glDisable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); } /*=======================================================*/ /* blend shading result to color buffer */ /*=======================================================*/ void GasBlendShadingResult(void) { /* In this function, shading result is blended to DISPLAY_BUFFER. */ /* Bind shading buffer and set viewport */ // glBindFramebuffer(GL_FRAMEBUFFER, DISPLAY_BUFFER); // glViewport(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); /* use gas accumulation related program */ glUseProgram(pPostId); /* Set fragment operation mode to GL_FRAGOP_MODE_GL_DMP */ glUniform1i(glGetUniformLocation(pPostId, "dmp_FragOperation.mode"), GL_FRAGOP_MODE_GL_DMP); /* Disable Fog Mode */ glUniform1i(glGetUniformLocation(pPostId, "dmp_Fog.mode"), GL_FALSE); /* * Setup texture and blending unit */ /* Bind gas texture (accumulation result) */ glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gastex); glUniform1i(glGetUniformLocation(pPostId, "dmp_Texture[0].samplerType"), GL_TEXTURE_2D); glUniform1i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].combineRgb"), GL_REPLACE); glUniform1i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].srcRgb"), GL_TEXTURE0, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].srcAlpha"), GL_TEXTURE0, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); glUniform1i(glGetUniformLocation(pPostId, "dmp_TexEnv[1].combineRgb"), GL_REPLACE); glUniform1i(glGetUniformLocation(pPostId, "dmp_TexEnv[1].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[1].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[1].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[1].srcRgb"), GL_PREVIOUS, GL_PREVIOUS, GL_PREVIOUS); glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[1].srcAlpha"), GL_PREVIOUS, GL_PREVIOUS, GL_PREVIOUS); /* misc settings */ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); glDepthMask(0); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glDisableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, gas.quad_vertBuf_ID); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, gas.quad_texBuf_ID); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gas.quad_index_ID); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); } /*=======================================================*/ /* render accumulation Z buffer */ /*=======================================================*/ void GasRenderAccumulationZBuffer(void (*drawfunc)(void)) { /* Bind accumulation buffer as a rendering target. */ glBindFramebuffer(GL_FRAMEBUFFER, gasbuf.acc); /* use shared / gas accumulation related program */ glUseProgram(pAccId); /* Clear z buffer */ glClearDepthf(1.f); glClear(GL_DEPTH_BUFFER_BIT); /* Set viewport */ glViewport(0, 0, GAS_ACC_WIDTH, GAS_ACC_HEIGHT); /* Disable color write */ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); /* draw object */ drawfunc(); /* Recover color mask */ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); }