/* * ShaderEngine.cpp * * Created on: Jul 18, 2008 * Author: pete */ #include #include #include "ShaderEngine.hpp" #include "BeatDetect.hpp" #include "Texture.hpp" #include "HLSLParser.h" #include "GLSLGenerator.h" #include "StaticGlShaders.h" #include // glm::mat4 #include // glm::translate, glm::rotate, glm::scale, glm::perspective #include #include #include #define FRAND ((rand() % 7381)/7380.0f) ShaderEngine::ShaderEngine() : presetCompShaderLoaded(false), presetWarpShaderLoaded(false) { std::shared_ptr static_gl_shaders = StaticGlShaders::Get(); programID_v2f_c4f = CompileShaderProgram( static_gl_shaders->GetV2fC4fVertexShader(), static_gl_shaders->GetV2fC4fFragmentShader(), "v2f_c4f"); programID_v2f_c4f_t2f = CompileShaderProgram( static_gl_shaders->GetV2fC4fT2fVertexShader(), static_gl_shaders->GetV2fC4fT2fFragmentShader(), "v2f_c4f_t2f"); programID_blur1 = CompileShaderProgram( static_gl_shaders->GetBlurVertexShader(), static_gl_shaders->GetBlur1FragmentShader(), "blur1"); programID_blur2 = CompileShaderProgram( static_gl_shaders->GetBlurVertexShader(), static_gl_shaders->GetBlur2FragmentShader(), "blur2"); uniform_v2f_c4f_vertex_tranformation = glGetUniformLocation(programID_v2f_c4f, "vertex_transformation"); uniform_v2f_c4f_vertex_point_size = glGetUniformLocation(programID_v2f_c4f, "vertex_point_size"); uniform_v2f_c4f_t2f_vertex_tranformation = glGetUniformLocation(programID_v2f_c4f_t2f, "vertex_transformation"); uniform_v2f_c4f_t2f_frag_texture_sampler = glGetUniformLocation(programID_v2f_c4f_t2f, "texture_sampler"); uniform_blur1_sampler = glGetUniformLocation(programID_blur1, "texture_sampler"); uniform_blur1_c0 = glGetUniformLocation(programID_blur1, "_c0"); uniform_blur1_c1 = glGetUniformLocation(programID_blur1, "_c1"); uniform_blur1_c2 = glGetUniformLocation(programID_blur1, "_c2"); uniform_blur1_c3 = glGetUniformLocation(programID_blur1, "_c3"); uniform_blur2_sampler = glGetUniformLocation(programID_blur2, "texture_sampler"); uniform_blur2_c0 = glGetUniformLocation(programID_blur2, "_c0"); uniform_blur2_c5 = glGetUniformLocation(programID_blur2, "_c5"); uniform_blur2_c6 = glGetUniformLocation(programID_blur2, "_c6"); // Initialize Blur vao/vbo float pointsBlur[16] = { -1.0, -1.0, 0.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0}; glGenBuffers(1, &vboBlur); glGenVertexArrays(1, &vaoBlur); glBindVertexArray(vaoBlur); glBindBuffer(GL_ARRAY_BUFFER, vboBlur); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8 * 2, pointsBlur, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float)*4, (void*)0); // Positions glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float)*4, (void*)(sizeof(float)*2)); // Textures glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); } ShaderEngine::~ShaderEngine() { glDeleteProgram(programID_v2f_c4f); glDeleteProgram(programID_v2f_c4f_t2f); glDeleteProgram(programID_blur1); glDeleteProgram(programID_blur2); glDeleteBuffers(1, &vboBlur); glDeleteVertexArrays(1, &vaoBlur); disablePresetShaders(); } bool ShaderEngine::checkCompileStatus(GLuint shader, const std::string & shaderTitle) { GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_TRUE) return true; // success int InfoLogLength; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &InfoLogLength); if ( InfoLogLength > 0 ){ std::vector FragmentShaderErrorMessage(InfoLogLength+1); glGetShaderInfoLog(shader, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); std::cerr << "Failed to compile shader '" << shaderTitle << "'. Error: " << &FragmentShaderErrorMessage[0] << std::endl; } return false; } void ShaderEngine::setParams(const int _texsizeX, const int _texsizeY, BeatDetect *_beatDetect, TextureManager *_textureManager) { this->beatDetect = _beatDetect; this->textureManager = _textureManager; aspectX = 1; aspectY = 1; if (_texsizeX > _texsizeY) aspectY = (float)_texsizeY/(float)_texsizeX; else aspectX = (float)_texsizeX/(float)_texsizeY; this->texsizeX = _texsizeX; this->texsizeY = _texsizeY; } // compile a user-defined shader from a preset. returns program ID if successful. GLuint ShaderEngine::compilePresetShader(const PresentShaderType shaderType, Shader &pmShader, const std::string &shaderFilename) { std::string program = pmShader.programSource; if (program.length() <= 0) return GL_FALSE; // replace "}" with return statement (this can probably be optimized for the GLSL conversion...) size_t found = program.rfind('}'); if (found != std::string::npos) { //std::cout << "last '}' found at: " << int(found) << std::endl; program.replace(int(found), 1, "_return_value = float4(ret.xyz, 1.0);\n" "}\n"); } else return GL_FALSE; // replace shader_body with entry point function found = program.find("shader_body"); if (found != std::string::npos) { //std::cout << "first 'shader_body' found at: " << int(found) << std::endl; if (shaderType == PresentWarpShader) { program.replace(int(found), 11, "void PS(float4 _vDiffuse : COLOR, float4 _uv : TEXCOORD0, float2 _rad_ang : TEXCOORD1, out float4 _return_value : COLOR)\n"); } else { program.replace(int(found), 11, "void PS(float4 _vDiffuse : COLOR, float2 _uv : TEXCOORD0, float2 _rad_ang : TEXCOORD1, out float4 _return_value : COLOR)\n"); } } else return GL_FALSE; // replace "{" with some variable declarations found = program.find('{',found); if (found != std::string::npos) { //std::cout << "first '{' found at: " << int(found) << std::endl; const char *progMain = \ "{\n" "float3 ret = 0;\n"; program.replace(int(found), 1, progMain); } else return GL_FALSE; pmShader.textures.clear(); // Add builtin textures pmShader.textures["main"] = textureManager->getTexture("main", GL_REPEAT, GL_LINEAR); pmShader.textures["fc_main"] = textureManager->getTexture("main", GL_CLAMP_TO_EDGE, GL_LINEAR); pmShader.textures["pc_main"] = textureManager->getTexture("main", GL_CLAMP_TO_EDGE, GL_NEAREST); pmShader.textures["fw_main"] = textureManager->getTexture("main", GL_REPEAT, GL_LINEAR); pmShader.textures["pw_main"] = textureManager->getTexture("main", GL_REPEAT, GL_NEAREST); pmShader.textures["noise_lq"] = textureManager->getTexture("noise_lq", GL_CLAMP_TO_EDGE, GL_LINEAR); pmShader.textures["noise_lq_lite"] = textureManager->getTexture("noise_lq_lite", GL_CLAMP_TO_EDGE, GL_LINEAR); pmShader.textures["noise_mq"] = textureManager->getTexture("noise_mq", GL_CLAMP_TO_EDGE, GL_LINEAR); pmShader.textures["noise_hq"] = textureManager->getTexture("noise_hq", GL_CLAMP_TO_EDGE, GL_LINEAR); pmShader.textures["noisevol_lq"] = textureManager->getTexture("noisevol_lq", GL_CLAMP_TO_EDGE, GL_LINEAR); pmShader.textures["noisevol_hq"] = textureManager->getTexture("noisevol_hq", GL_CLAMP_TO_EDGE, GL_LINEAR); // set up texture samplers for all samplers references in the shader program found = 0; found = program.find("sampler_", found); while (found != std::string::npos) { found += 8; size_t end = program.find_first_of(" ;,\n\r)", found); if (end != std::string::npos) { std::string sampler = program.substr((int) found, (int) end - found); std::string lowerCaseName(sampler); std::transform(lowerCaseName.begin(), lowerCaseName.end(), lowerCaseName.begin(), tolower); TextureSamplerDesc texDesc = textureManager->getTexture(sampler, GL_REPEAT, GL_LINEAR); if (texDesc.first == NULL) { if (lowerCaseName.substr(0, 4) == "rand" || lowerCaseName.substr(2, 5) == "_rand") { texDesc = textureManager->getRandomTextureName(sampler); } else { texDesc = textureManager->tryLoadingTexture(sampler); } } if (texDesc.first == NULL) { std::cerr << "Texture loading error for: " << sampler << std::endl; } else { std::map::const_iterator iter = pmShader.textures.cbegin(); for ( ; iter != pmShader.textures.cend(); ++iter) { if (iter->first == sampler) break; } if (iter == pmShader.textures.cend()) pmShader.textures[sampler] = texDesc; } } found = program.find("sampler_", found); } textureManager->clearRandomTextures(); found = program.find("GetBlur3"); if (found != std::string::npos) { blur1_enabled = blur2_enabled = blur3_enabled = true; pmShader.textures["blur3"] = textureManager->getTexture("blur3", GL_CLAMP_TO_EDGE, GL_LINEAR); pmShader.textures["blur2"] = textureManager->getTexture("blur2", GL_CLAMP_TO_EDGE, GL_LINEAR); pmShader.textures["blur1"] = textureManager->getTexture("blur1", GL_CLAMP_TO_EDGE, GL_LINEAR); } else { found = program.find("GetBlur2"); if (found != std::string::npos) { blur1_enabled = blur2_enabled = true; pmShader.textures["blur2"] = textureManager->getTexture("blur2", GL_CLAMP_TO_EDGE, GL_LINEAR); pmShader.textures["blur1"] = textureManager->getTexture("blur1", GL_CLAMP_TO_EDGE, GL_LINEAR); } else { found = program.find("GetBlur1"); if (found != std::string::npos) { blur1_enabled = true; pmShader.textures["blur1"] = textureManager->getTexture("blur1", GL_CLAMP_TO_EDGE, GL_LINEAR); } } } std::string fullSource; // prepend our HLSL template to the actual program source fullSource.append(StaticGlShaders::Get()->GetPresetShaderHeader()); if (shaderType == PresentWarpShader) { fullSource.append( "#define rad _rad_ang.x\n" "#define ang _rad_ang.y\n" "#define uv _uv.xy\n" "#define uv_orig _uv.zw\n"); } else { fullSource.append( "#define rad _rad_ang.x\n" "#define ang _rad_ang.y\n" "#define uv _uv.xy\n" "#define uv_orig _uv.xy\n" "#define hue_shader _vDiffuse.xyz\n"); } fullSource.append(program); std::string shaderTypeString; switch(shaderType) { case PresentWarpShader: shaderTypeString = "Warp"; break; case PresentCompositeShader: shaderTypeString = "Comp"; break; case PresentBlur1Shader: shaderTypeString = "Blur1"; break; case PresentBlur2Shader: shaderTypeString = "Blur2"; break; default: shaderTypeString = "Other"; } M4::GLSLGenerator generator; M4::Allocator allocator; M4::HLSLTree tree( &allocator ); M4::HLSLParser parser(&allocator, &tree); // preprocess define macros std::string sourcePreprocessed; if (!parser.ApplyPreprocessor(shaderFilename.c_str(), fullSource.c_str(), fullSource.size(), sourcePreprocessed)) { std::cerr << "Failed to preprocess HLSL(step1) " << shaderTypeString << " shader" << std::endl; #if !DUMP_SHADERS_ON_ERROR std::cerr << "Source: " << std::endl << fullSource << std::endl; #else std::ofstream out("/tmp/shader_" + shaderTypeString + "_step1.txt"); out << fullSource; out.close(); #endif return GL_FALSE; } // Remove previous shader declarations std::smatch matches; while(std::regex_search(sourcePreprocessed, matches, std::regex("sampler(2D|3D|)(\\s+|\\().*"))) { sourcePreprocessed.replace(matches.position(), matches.length(), ""); } // Remove previous texsize declarations while(std::regex_search(sourcePreprocessed, matches, std::regex("float4\\s+texsize_.*"))) { sourcePreprocessed.replace(matches.position(), matches.length(), ""); } // Declare samplers std::set texsizes; std::map::const_iterator iter_samplers = pmShader.textures.cbegin(); for ( ; iter_samplers != pmShader.textures.cend(); ++iter_samplers) { Texture * texture = iter_samplers->second.first; if (texture->type == GL_TEXTURE_3D) { sourcePreprocessed.insert(0, "uniform sampler3D sampler_" + iter_samplers->first + ";\n"); } else { sourcePreprocessed.insert(0, "uniform sampler2D sampler_" + iter_samplers->first + ";\n"); } texsizes.insert(iter_samplers->first); texsizes.insert(texture->name); } // Declare texsizes std::set::const_iterator iter_texsizes = texsizes.cbegin(); for ( ; iter_texsizes != texsizes.cend(); ++iter_texsizes) { sourcePreprocessed.insert(0, "uniform float4 texsize_" + *iter_texsizes + ";\n"); } // transpile from HLSL (aka preset shader aka directX shader) to GLSL (aka OpenGL shader lang) // parse if( !parser.Parse(shaderFilename.c_str(), sourcePreprocessed.c_str(), sourcePreprocessed.size()) ) { std::cerr << "Failed to parse HLSL(step2) " << shaderTypeString << " shader" << std::endl; #if !DUMP_SHADERS_ON_ERROR std::cerr << "Source: " << std::endl << sourcePreprocessed << std::endl; #else std::ofstream out2("/tmp/shader_" + shaderTypeString + "_step2.txt"); out2 << sourcePreprocessed; out2.close(); #endif return GL_FALSE; } // generate GLSL if (!generator.Generate(&tree, M4::GLSLGenerator::Target_FragmentShader, StaticGlShaders::Get()->GetGlslGeneratorVersion(), "PS")) { std::cerr << "Failed to transpile HLSL(step3) " << shaderTypeString << " shader to GLSL" << std::endl; #if !DUMP_SHADERS_ON_ERROR std::cerr << "Source: " << std::endl << sourcePreprocessed << std::endl; #else std::ofstream out2("/tmp/shader_" + shaderTypeString + "_step2.txt"); out2 << sourcePreprocessed; out2.close(); #endif return GL_FALSE; } // now we have GLSL source for the preset shader program (hopefully it's // valid!) copmile the preset shader fragment shader with the standard // vertex shader and cross our fingers GLuint ret = 0; if (shaderType == PresentWarpShader) { ret = CompileShaderProgram( StaticGlShaders::Get()->GetPresetWarpVertexShader(), generator.GetResult(), shaderTypeString); } else { ret = CompileShaderProgram( StaticGlShaders::Get()->GetPresetCompVertexShader(), generator.GetResult(), shaderTypeString); } if (ret != GL_FALSE) { #ifdef DEBUG std::cerr << "Successful compilation of " << shaderTypeString << std::endl; #endif } else { std::cerr << "Compilation error (step3) of " << shaderTypeString << std::endl; #if !DUMP_SHADERS_ON_ERROR std::cerr << "Source:" << std::endl << generator.GetResult() << std::endl; #else std::ofstream out3("/tmp/shader_" + shaderTypeString + "_step3.txt"); out3 << generator.GetResult(); out3.close(); #endif } return ret; } void ShaderEngine::SetupShaderVariables(GLuint program, const Pipeline &pipeline, const PipelineContext &context) { // pass info from projectM to the shader uniforms // these are the inputs: http://www.geisswerks.com/milkdrop/milkdrop_preset_authoring.html#3f6 float time_since_preset_start = context.time - context.presetStartTime; float time_since_preset_start_wrapped = time_since_preset_start - (int)(time_since_preset_start/10000)*10000; float mip_x = logf((float)texsizeX)/logf(2.0f); float mip_y = logf((float)texsizeX)/logf(2.0f); float mip_avg = 0.5f*(mip_x + mip_y); glUniform4f(glGetUniformLocation(program, "rand_frame"), (rand() % 100) * .01, (rand() % 100) * .01, (rand()% 100) * .01, (rand() % 100) * .01); glUniform4f(glGetUniformLocation(program, "rand_preset"), rand_preset[0], rand_preset[1], rand_preset[2], rand_preset[3]); glUniform4f(glGetUniformLocation(program, "_c0"), aspectX, aspectY, 1 / aspectX, 1 / aspectY); glUniform4f(glGetUniformLocation(program, "_c1"), 0.0, 0.0, 0.0, 0.0); glUniform4f(glGetUniformLocation(program, "_c2"), time_since_preset_start_wrapped, context.fps, context.frame, context.progress); glUniform4f(glGetUniformLocation(program, "_c3"), beatDetect->bass/100, beatDetect->mid/100, beatDetect->treb/100, beatDetect->vol/100); glUniform4f(glGetUniformLocation(program, "_c4"), beatDetect->bass_att/100, beatDetect->mid_att/100, beatDetect->treb_att/100, beatDetect->vol_att/100); glUniform4f(glGetUniformLocation(program, "_c5"), pipeline.blur1x-pipeline.blur1n, pipeline.blur1n, pipeline.blur2x-pipeline.blur2n, pipeline.blur2n); glUniform4f(glGetUniformLocation(program, "_c6"), pipeline.blur3x-pipeline.blur3n, pipeline.blur3n, pipeline.blur1n, pipeline.blur1x); glUniform4f(glGetUniformLocation(program, "_c7"), texsizeX, texsizeY, 1 / (float) texsizeX, 1 / (float) texsizeY); glUniform4f(glGetUniformLocation(program, "_c8"), 0.5f+0.5f*cosf(context.time* 0.329f+1.2f), 0.5f+0.5f*cosf(context.time* 1.293f+3.9f), 0.5f+0.5f*cosf(context.time* 5.070f+2.5f), 0.5f+0.5f*cosf(context.time*20.051f+5.4f)); glUniform4f(glGetUniformLocation(program, "_c9"), 0.5f+0.5f*sinf(context.time* 0.329f+1.2f), 0.5f+0.5f*sinf(context.time* 1.293f+3.9f), 0.5f+0.5f*sinf(context.time* 5.070f+2.5f), 0.5f+0.5f*sinf(context.time*20.051f+5.4f)); glUniform4f(glGetUniformLocation(program, "_c10"), 0.5f+0.5f*cosf(context.time*0.0050f+2.7f), 0.5f+0.5f*cosf(context.time*0.0085f+5.3f), 0.5f+0.5f*cosf(context.time*0.0133f+4.5f), 0.5f+0.5f*cosf(context.time*0.0217f+3.8f)); glUniform4f(glGetUniformLocation(program, "_c11"), 0.5f+0.5f*sinf(context.time*0.0050f+2.7f), 0.5f+0.5f*sinf(context.time*0.0085f+5.3f), 0.5f+0.5f*sinf(context.time*0.0133f+4.5f), 0.5f+0.5f*sinf(context.time*0.0217f+3.8f)); glUniform4f(glGetUniformLocation(program, "_c12"), mip_x, mip_y, mip_avg, 0 ); glUniform4f(glGetUniformLocation(program, "_c13"), pipeline.blur2n, pipeline.blur2x, pipeline.blur3n, pipeline.blur3x); glm::mat4 temp_mat[24]; // write matrices for (int i=0; i<20; i++) { glm::mat4 mx, my, mz, mxlate; mx = glm::rotate(glm::mat4(1.0f), rot_base[i].x + rot_speed[i].x*context.time, glm::vec3(1.0f, 0.0f, 0.0f)); my = glm::rotate(glm::mat4(1.0f), rot_base[i].y + rot_speed[i].y*context.time, glm::vec3(0.0f, 1.0f, 0.0f)); mz = glm::rotate(glm::mat4(1.0f), rot_base[i].z + rot_speed[i].z*context.time, glm::vec3(0.0f, 0.0f, 1.0f)); mxlate = glm::translate(glm::mat4(1.0f), glm::vec3(xlate[i].x, xlate[i].y, xlate[i].z)); temp_mat[i] = mxlate * mx; temp_mat[i] = mz * temp_mat[i]; temp_mat[i] = my * temp_mat[i]; } // the last 4 are totally random, each frame for (int i=20; i<24; i++) { glm::mat4 mx, my, mz, mxlate; mx = glm::rotate(glm::mat4(1.0f), FRAND * 6.28f, glm::vec3(1.0f, 0.0f, 0.0f)); my = glm::rotate(glm::mat4(1.0f), FRAND * 6.28f, glm::vec3(0.0f, 1.0f, 0.0f)); mz = glm::rotate(glm::mat4(1.0f), FRAND * 6.28f, glm::vec3(0.0f, 0.0f, 1.0f)); mxlate = glm::translate(glm::mat4(1.0f), glm::vec3(FRAND, FRAND, FRAND)); temp_mat[i] = mxlate * mx; temp_mat[i] = mz * temp_mat[i]; temp_mat[i] = my * temp_mat[i]; } glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_s1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[0]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_s2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[1]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_s3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[2]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_s4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[3]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_d1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[4]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_d2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[5]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_d3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[6]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_d4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[7]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_f1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[8]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_f2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[9]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_f3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[10]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_f4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[11]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_vf1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[12]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_vf2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[13]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_vf3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[14]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_vf4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[15]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_uf1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[16]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_uf2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[17]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_uf3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[18]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_uf4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[19]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_rand1"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[20]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_rand2"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[21]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_rand3"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[22]))); glUniformMatrix3x4fv(glGetUniformLocation(program, "rot_rand4"), 1, GL_FALSE, glm::value_ptr(glm::mat3x4(temp_mat[23]))); // set program uniform "_q[a-h]" values (_qa.x, _qa.y, _qa.z, _qa.w, _qb.x, _qb.y ... ) alias q[1-32] for (int i=0; i < 32; i+=4) { std::string varName = "_q"; varName.push_back('a' + i/4); glUniform4f(glGetUniformLocation(program, varName.c_str()), pipeline.q[i], pipeline.q[i+1], pipeline.q[i+2], pipeline.q[i+3]); } } void ShaderEngine::SetupTextures(GLuint program, const Shader &shader) { unsigned int texNum = 0; std::map texsizes; // Set samplers for (std::map::const_iterator iter_samplers = shader.textures.begin(); iter_samplers != shader.textures.end(); ++iter_samplers) { std::string texName = iter_samplers->first; Texture * texture = iter_samplers->second.first; Sampler * sampler = iter_samplers->second.second; std::string samplerName = "sampler_" + texName; // https://www.khronos.org/opengl/wiki/Sampler_(GLSL)#Binding_textures_to_samplers GLint param = glGetUniformLocation(program, samplerName.c_str()); if (param < 0) { // unused uniform have been optimized out by glsl compiler continue; } texsizes[texName] = texture; texsizes[texture->name] = texture; glActiveTexture(GL_TEXTURE0 + texNum); glBindTexture(texture->type, texture->texID); glBindSampler(texNum, sampler->samplerID); glUniform1i(param, texNum); texNum++; } // Set texsizes std::map::const_iterator iter_textures = texsizes.cbegin(); for ( ; iter_textures != texsizes.cend(); ++iter_textures) { Texture * texture = iter_textures->second; std::string texsizeName = "texsize_" + iter_textures->first; GLint textSizeParam = glGetUniformLocation(program, texsizeName.c_str()); if (textSizeParam >= 0) { glUniform4f(textSizeParam, texture->width, texture->height, 1 / (float) texture->width, 1 / (float) texture->height); } else { // unused uniform have been optimized out by glsl compiler continue; } } } void ShaderEngine::RenderBlurTextures(const Pipeline &pipeline, const PipelineContext &pipelineContext) { unsigned int passes = 0; if (blur3_enabled) { passes = 6; } else if (blur2_enabled) { passes = 4; } else if (blur1_enabled) { passes = 2; } else { return; } const float w[8] = { 4.0f, 3.8f, 3.5f, 2.9f, 1.9f, 1.2f, 0.7f, 0.3f }; //<- user can specify these float edge_darken = pipeline.blur1ed; float blur_min[3], blur_max[3]; blur_min[0] = pipeline.blur1n; blur_min[1] = pipeline.blur2n; blur_min[2] = pipeline.blur3n; blur_max[0] = pipeline.blur1x; blur_max[1] = pipeline.blur2x; blur_max[2] = pipeline.blur3x; // check that precision isn't wasted in later blur passes [...min-max gap can't grow!] // also, if min-max are close to each other, push them apart: const float fMinDist = 0.1f; if (blur_max[0] - blur_min[0] < fMinDist) { float avg = (blur_min[0] + blur_max[0])*0.5f; blur_min[0] = avg - fMinDist*0.5f; blur_max[0] = avg - fMinDist*0.5f; } blur_max[1] = std::min(blur_max[0], blur_max[1]); blur_min[1] = std::max(blur_min[0], blur_min[1]); if (blur_max[1] - blur_min[1] < fMinDist) { float avg = (blur_min[1] + blur_max[1])*0.5f; blur_min[1] = avg - fMinDist*0.5f; blur_max[1] = avg - fMinDist*0.5f; } blur_max[2] = std::min(blur_max[1], blur_max[2]); blur_min[2] = std::max(blur_min[1], blur_min[2]); if (blur_max[2] - blur_min[2] < fMinDist) { float avg = (blur_min[2] + blur_max[2])*0.5f; blur_min[2] = avg - fMinDist*0.5f; blur_max[2] = avg - fMinDist*0.5f; } float fscale[3]; float fbias[3]; // figure out the progressive scale & bias needed, at each step, // to go from one [min..max] range to the next. float temp_min, temp_max; fscale[0] = 1.0f / (blur_max[0] - blur_min[0]); fbias [0] = -blur_min[0] * fscale[0]; temp_min = (blur_min[1] - blur_min[0]) / (blur_max[0] - blur_min[0]); temp_max = (blur_max[1] - blur_min[0]) / (blur_max[0] - blur_min[0]); fscale[1] = 1.0f / (temp_max - temp_min); fbias [1] = -temp_min * fscale[1]; temp_min = (blur_min[2] - blur_min[1]) / (blur_max[1] - blur_min[1]); temp_max = (blur_max[2] - blur_min[1]) / (blur_max[1] - blur_min[1]); fscale[2] = 1.0f / (temp_max - temp_min); fbias [2] = -temp_min * fscale[2]; const std::vector & blurTextures = textureManager->getBlurTextures(); const Texture * mainTexture = textureManager->getMainTexture(); glBlendFunc(GL_ONE, GL_ZERO); glBindVertexArray(vaoBlur); for (unsigned int i=0; iwidth, blurTextures[i]->height); // hook up correct source texture - assume there is only one, at stage 0 glActiveTexture(GL_TEXTURE0); if (i == 0) { glBindTexture(GL_TEXTURE_2D, mainTexture->texID); } else { glBindTexture(GL_TEXTURE_2D, blurTextures[i-1]->texID); } float srcw = (i==0) ? mainTexture->width : blurTextures[i-1]->width; float srch = (i==0) ? mainTexture->height : blurTextures[i-1]->height; float fscale_now = fscale[i/2]; float fbias_now = fbias[i/2]; // set constants if (i%2==0) { // pass 1 (long horizontal pass) //------------------------------------- const float w1 = w[0] + w[1]; const float w2 = w[2] + w[3]; const float w3 = w[4] + w[5]; const float w4 = w[6] + w[7]; const float d1 = 0 + 2*w[1]/w1; const float d2 = 2 + 2*w[3]/w2; const float d3 = 4 + 2*w[5]/w3; const float d4 = 6 + 2*w[7]/w4; const float w_div = 0.5f/(w1+w2+w3+w4); //------------------------------------- //float4 _c0; // source texsize (.xy), and inverse (.zw) //float4 _c1; // w1..w4 //float4 _c2; // d1..d4 //float4 _c3; // scale, bias, w_div, 0 //------------------------------------- glUniform4f(uniform_blur1_c0, srcw, srch, 1.0f/srcw, 1.0f/srch); glUniform4f(uniform_blur1_c1, w1,w2,w3,w4); glUniform4f(uniform_blur1_c2, d1,d2,d3,d4); glUniform4f(uniform_blur1_c3, fscale_now, fbias_now, w_div, 0.0); } else { // pass 2 (short vertical pass) //------------------------------------- const float w1 = w[0]+w[1] + w[2]+w[3]; const float w2 = w[4]+w[5] + w[6]+w[7]; const float d1 = 0 + 2*((w[2]+w[3])/w1); const float d2 = 2 + 2*((w[6]+w[7])/w2); const float w_div = 1.0f/((w1+w2)*2); //------------------------------------- //float4 _c0; // source texsize (.xy), and inverse (.zw) //float4 _c5; // w1,w2,d1,d2 //float4 _c6; // w_div, edge_darken_c1, edge_darken_c2, edge_darken_c3 //------------------------------------- glUniform4f(uniform_blur2_c0, srcw, srch, 1.0f/srcw, 1.0f/srch); glUniform4f(uniform_blur2_c5, w1,w2,d1,d2); // note: only do this first time; if you do it many times, // then the super-blurred levels will have big black lines along the top & left sides. if (i==1) glUniform4f(uniform_blur2_c6, w_div, (1-edge_darken), edge_darken, 5.0f); //darken edges else glUniform4f(uniform_blur2_c6, w_div, 1.0f, 0.0f, 5.0f); // don't darken } // draw fullscreen quad glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // save to blur texture glBindTexture(GL_TEXTURE_2D, blurTextures[i]->texID); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, blurTextures[i]->width, blurTextures[i]->height); } glBindVertexArray(0); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBindTexture(GL_TEXTURE_2D, 0); } bool ShaderEngine::linkProgram(GLuint programID) { glLinkProgram(programID); GLint program_linked; glGetProgramiv(programID, GL_LINK_STATUS, &program_linked); if (program_linked == GL_TRUE) { return true; // success } int InfoLogLength; glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &InfoLogLength); if ( InfoLogLength > 0 ){ std::vector ProgramErrorMessage(InfoLogLength+1); glGetProgramInfoLog(programID, InfoLogLength, NULL, &ProgramErrorMessage[0]); std::cerr << "Failed to link program: " << &ProgramErrorMessage[0] << std::endl; } return false; } bool ShaderEngine::loadPresetShaders(Pipeline &pipeline, const std::string & presetName) { bool ok = true; // blur programs blur1_enabled = false; blur2_enabled = false; blur3_enabled = false; m_presetName = presetName; // compile and link warp and composite shaders from pipeline if (!pipeline.warpShader.programSource.empty()) { programID_presetWarp = loadPresetShader(PresentWarpShader, pipeline.warpShader, pipeline.warpShaderFilename); if (programID_presetWarp != GL_FALSE) { uniform_vertex_transf_warp_shader = glGetUniformLocation(programID_presetWarp, "vertex_transformation"); presetWarpShaderLoaded = true; } else { ok = false; } } if (!pipeline.compositeShader.programSource.empty()) { programID_presetComp = loadPresetShader(PresentCompositeShader, pipeline.compositeShader, pipeline.compositeShaderFilename); if (programID_presetComp != GL_FALSE) { presetCompShaderLoaded = true; } else { ok = false; } } // std::cout << "Preset composite shader active: " << presetCompShaderLoaded << ", preset warp shader active: " << presetWarpShaderLoaded << std::endl; return ok; } GLuint ShaderEngine::loadPresetShader(const ShaderEngine::PresentShaderType shaderType, Shader &presetShader, std::string &shaderFilename) { GLuint program = compilePresetShader(shaderType, presetShader, shaderFilename); if (program == GL_FALSE) { // failed to compile return GL_FALSE; } return program; } // deactivate preset shaders void ShaderEngine::disablePresetShaders() { if (presetCompShaderLoaded) glDeleteProgram(programID_presetComp); if (presetWarpShaderLoaded) glDeleteProgram(programID_presetWarp); presetCompShaderLoaded = false; presetWarpShaderLoaded = false; } void ShaderEngine::reset() { disablePresetShaders(); rand_preset[0] = FRAND; rand_preset[1] = FRAND; rand_preset[2] = FRAND; rand_preset[3] = FRAND; unsigned int k = 0; do { for (int i=0; i<4; i++) { float xlate_mult = 1; float rot_mult = 0.9f*powf(k/8.0f, 3.2f); xlate[k].x = (FRAND*2-1)*xlate_mult; xlate[k].y = (FRAND*2-1)*xlate_mult; xlate[k].z = (FRAND*2-1)*xlate_mult; rot_base[k].x = FRAND*6.28f; rot_base[k].y = FRAND*6.28f; rot_base[k].z = FRAND*6.28f; rot_speed[k].x = (FRAND*2-1)*rot_mult; rot_speed[k].y = (FRAND*2-1)*rot_mult; rot_speed[k].z = (FRAND*2-1)*rot_mult; k++; } } while (k < sizeof(xlate)/sizeof(xlate[0])); } GLuint ShaderEngine::CompileShaderProgram(const std::string & VertexShaderCode, const std::string & FragmentShaderCode, const std::string & shaderTypeString){ #if defined(WIN32) && !defined(EYETUNE_WINRT) GLenum err = glewInit(); #endif /** WIN32 */ // Create the shaders GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); // Compile Vertex Shader char const * VertexSourcePointer = VertexShaderCode.c_str(); glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL); glCompileShader(VertexShaderID); checkCompileStatus(VertexShaderID, "Vertex: " + shaderTypeString); // Compile Fragment Shader char const * FragmentSourcePointer = FragmentShaderCode.c_str(); glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL); glCompileShader(FragmentShaderID); checkCompileStatus(FragmentShaderID, "Fragment: " + shaderTypeString); // Link the program GLuint programID = glCreateProgram(); glAttachShader(programID, VertexShaderID); glAttachShader(programID, FragmentShaderID); bool linkOK = linkProgram(programID); glDetachShader(programID, VertexShaderID); glDetachShader(programID, FragmentShaderID); glDeleteShader(VertexShaderID); glDeleteShader(FragmentShaderID); return linkOK ? programID : GL_FALSE; } void ShaderEngine::validateProgram(const GLuint programID) { GLint Result = GL_FALSE; int InfoLogLength; glValidateProgram(programID); // Check the program glGetProgramiv(programID, GL_VALIDATE_STATUS, &Result); glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &InfoLogLength); if ( InfoLogLength > 0 ){ std::vector ProgramErrorMessage(InfoLogLength+1); glGetProgramInfoLog(programID, InfoLogLength, NULL, &ProgramErrorMessage[0]); fprintf(stderr, "%s\n", &ProgramErrorMessage[0]); } } // use the appropriate shader program for rendering the interpolation. // it will use the preset shader if available, otherwise the textured shader bool ShaderEngine::enableWarpShader(Shader &shader, const Pipeline &pipeline, const PipelineContext &pipelineContext, const glm::mat4 & mat_ortho) { if (presetWarpShaderLoaded) { glUseProgram(programID_presetWarp); SetupTextures(programID_presetWarp, shader); SetupShaderVariables(programID_presetWarp, pipeline, pipelineContext); glUniformMatrix4fv(uniform_vertex_transf_warp_shader, 1, GL_FALSE, glm::value_ptr(mat_ortho)); #if OGL_DEBUG validateProgram(programID_presetWarp); #endif return true; } glUseProgram(programID_v2f_c4f_t2f); glUniformMatrix4fv(uniform_v2f_c4f_t2f_vertex_tranformation, 1, GL_FALSE, glm::value_ptr(mat_ortho)); glUniform1i(uniform_v2f_c4f_t2f_frag_texture_sampler, 0); return false; } bool ShaderEngine::enableCompositeShader(Shader &shader, const Pipeline &pipeline, const PipelineContext &pipelineContext) { if (presetCompShaderLoaded) { glUseProgram(programID_presetComp); SetupTextures(programID_presetComp, shader); SetupShaderVariables(programID_presetComp, pipeline, pipelineContext); #if OGL_DEBUG validateProgram(programID_presetComp); #endif return true; } glUseProgram(programID_v2f_c4f_t2f); return false; }