webgl::ShaderValidator* WebGLContext::CreateShaderValidator(GLenum shaderType) const { if (mBypassShaderValidation) return nullptr; ShShaderSpec spec = IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC; ShShaderOutput outputLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT; // If we're using WebGL2 we want a more specific version of GLSL if (IsWebGL2()) outputLanguage = ShaderOutput(gl); ShBuiltInResources resources; memset(&resources, 0, sizeof(resources)); ShInitBuiltInResources(&resources); resources.HashFunction = webgl::IdentifierHashFunc; resources.MaxVertexAttribs = mGLMaxVertexAttribs; resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors; resources.MaxVaryingVectors = mGLMaxVaryingVectors; resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits; resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits; resources.MaxTextureImageUnits = mGLMaxTextureImageUnits; resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors; resources.MaxDrawBuffers = mGLMaxDrawBuffers; if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth)) resources.EXT_frag_depth = 1; if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) resources.OES_standard_derivatives = 1; if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) resources.EXT_draw_buffers = 1; if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod)) resources.EXT_shader_texture_lod = 1; // Tell ANGLE to allow highp in frag shaders. (unless disabled) // If underlying GLES doesn't have highp in frag shaders, it should complain anyways. resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1; if (gl->WorkAroundDriverBugs()) { #ifdef XP_MACOSX if (gl->Vendor() == gl::GLVendor::NVIDIA) { // Work around bug 890432 resources.MaxExpressionComplexity = 1000; } #endif } int compileOptions = webgl::ChooseValidatorCompileOptions(resources, gl); return webgl::ShaderValidator::Create(shaderType, spec, outputLanguage, resources, compileOptions); }
bool BulletContent::Initialize(std::string& err) { typedef VertexPosUV BulletVertex; RenderIOAttributes vertIns = BulletVertex::GetVertexAttributes(); #pragma region Meshes for (unsigned int i = 0; i < B_NUMBER_OF_BULLETS; ++i) { bulletMesh.SubMeshes.push_back(MeshData(false, PT_TRIANGLE_LIST)); } std::vector<BulletVertex> vertices; std::vector<unsigned int> indices; Assimp::Importer importer; unsigned int flags = aiProcessPreset_TargetRealtime_MaxQuality; for (unsigned int i = 0; i < B_NUMBER_OF_BULLETS; ++i) { //Get the file for this bullet type. std::string file = "Content/Game/Meshes/Bullets/"; switch ((Bullets)i) { case B_PUNCHER: file += "Puncher.obj"; break; case B_TERRIBLE_SHOTGUN: file += "Terrible Shotgun.obj"; break; case B_SPRAY_N_PRAY: file += "Spray and Pray.obj"; break; case B_CLUSTER: file += "Cluster.obj"; break; default: assert(false); } const aiScene* scene = importer.ReadFile(file, flags); //Make sure the scene is valid. if (scene == 0) { err = "Error loading '" + file + "': " + importer.GetErrorString(); return false; } if (scene->mNumMeshes != 1) { err = "Mesh '" + file + "' has " + std::to_string(scene->mNumMeshes) + " meshes in it"; return false; } aiMesh* mesh = scene->mMeshes[0]; //Make sure the mesh is valid. assert(mesh->HasFaces()); if (!mesh->HasPositions() || !mesh->HasTextureCoords(0)) { err = "Mesh '" + file + "' is missing positions or UVs!"; return false; } //Populate the vertex/index buffer data. vertices.resize(mesh->mNumVertices); for (unsigned int j = 0; j < mesh->mNumVertices; ++j) { vertices[j].Pos = *(Vector3f*)(&mesh->mVertices[j].x); vertices[j].UV = *(Vector2f*)(&mesh->mTextureCoords[0][j].x); } indices.resize(mesh->mNumFaces * 3); for (unsigned int j = 0; j < mesh->mNumFaces; ++j) { aiFace& fce = mesh->mFaces[j]; if (fce.mNumIndices != 3) { err = "A face in mesh '" + file + "' has a non-tri face with " + std::to_string(fce.mNumIndices) + " indices!"; return false; } indices[(j * 3)] = fce.mIndices[0]; indices[(j * 3) + 1] = fce.mIndices[1]; indices[(j * 3) + 2] = fce.mIndices[2]; } //Create the vertex/index buffers. bulletMesh.SubMeshes[i].SetVertexData(vertices, MeshData::BUF_STATIC, vertIns); bulletMesh.SubMeshes[i].SetIndexData(indices, MeshData::BUF_STATIC); } #pragma endregion #pragma region Textures { Array2D<Vector4b> values(1, 1, Vector4b((unsigned char)255, 255, 255, 255)); defaultTex.Create(); defaultTex.SetColorData(values); } #pragma endregion #pragma region Materials { SerializedMaterial serMat; serMat.VertexInputs = vertIns; DataLine vIn_Pos(VertexInputNode::GetInstance(), 0), vIn_UV(VertexInputNode::GetInstance(), 1); DataNode::Ptr screenPos = SpaceConverterNode::ObjPosToScreenPos(vIn_Pos, "screenPos"); serMat.MaterialOuts.VertexPosOutput = DataLine(screenPos, 1); serMat.MaterialOuts.VertexOutputs.push_back(ShaderOutput("fIn_UV", vIn_UV)); DataLine fIn_UV(FragmentInputNode::GetInstance(), 0); DataNode::Ptr tex(new TextureSample2DNode(fIn_UV, UNIFORM_TEXTURE, "texSample")); DataLine texRGB(tex, TextureSample2DNode::GetOutputIndex(CO_AllColorChannels)); DataNode::Ptr colorParam(new ParamNode(3, UNIFORM_COLOR)); DataNode::Ptr finalRGB(new MultiplyNode(texRGB, colorParam, "finalRGB")); DataNode::Ptr finalColor(new CombineVectorNode(finalRGB, 1.0f, "finalColor")); serMat.MaterialOuts.FragmentOutputs.push_back(ShaderOutput("fOut_Color", finalColor)); auto genMat = ShaderGenerator::GenerateMaterial(serMat, bulletParams, BlendMode::GetOpaque()); if (!genMat.ErrorMessage.empty()) { err = "Error generating bullet material: " + genMat.ErrorMessage; return false; } bulletMat = genMat.Mat; } #pragma endregion return true; }
std::string TR::InitializeSystem(void) { if (textRenderer != 0) { return "System was already initialized."; } ClearAllRenderingErrors(); tempTex.Create(); //Transform matrices and render info. textRendererCam.GetViewTransform(viewMat); textRendererCam.GetOrthoProjection(projMat); textRendererInfo = RenderInfo(0.0f, &textRendererCam, &viewMat, &projMat); //Material. textRendererParams.clear(); SerializedMaterial matData(DrawingQuad::GetVertexInputData()); //Use a simple vertex shader that just uses world position -- in other words, // the visible range in world space is just the volume from {-1, -1, -1} to {1, 1, 1}. //It also outputs UVs for the fragment shader to use. DataLine vIn_Pos(VertexInputNode::GetInstance(), 0), vIn_UV(VertexInputNode::GetInstance(), 1); DataNode::Ptr objPosToWorld(new SpaceConverterNode(DataLine(VertexInputNode::GetInstanceName()), SpaceConverterNode::ST_OBJECT, SpaceConverterNode::ST_WORLD, SpaceConverterNode::DT_POSITION, "objPosToWorld")); DataNode::Ptr vertexPosOut(new CombineVectorNode(DataLine(objPosToWorld->GetName()), DataLine(1.0f))); matData.MaterialOuts.VertexPosOutput = vertexPosOut; //The fragment shader just samples from the texture containing the text char // and uses its "red" value because it's a grayscale texture. matData.MaterialOuts.VertexOutputs.push_back(ShaderOutput("vOut_UV", vIn_UV)); DataLine fIn_UV(FragmentInputNode::GetInstance(), 0); DataNode::Ptr textSampler(new TextureSample2DNode(fIn_UV, textSamplerName, "textSampler")); DataLine textSamplerRGBA(textSampler, TextureSample2DNode::GetOutputIndex(CO_AllChannels)); DataNode::Ptr textColor(new SwizzleNode(textSamplerRGBA, SwizzleNode::C_X, SwizzleNode::C_X, SwizzleNode::C_X, SwizzleNode::C_X, "swizzleTextSample")); matData.MaterialOuts.FragmentOutputs.push_back(ShaderOutput("fOut_Color", textColor)); BlendMode blending = BlendMode::GetTransparent(); ShaderGenerator::GeneratedMaterial genM = ShaderGenerator::GenerateMaterial(matData, textRendererParams, blending); if (!genM.ErrorMessage.empty()) { return "Error generating text renderer material: " + genM.ErrorMessage; } textRenderer = genM.Mat; return ""; }
PostProcessChain::PostProcessChain(std::vector<std::shared_ptr<PostProcessEffect>> effectChain, unsigned int screenWidth, unsigned int screenHeight, bool useMipmaps, const TextureSampleSettings2D & renderTargetSettings, PixelSizes pixelSize, RenderTargetManager & manager) : rtManager(manager), rt1(RenderTargetManager::ERROR_ID), rt2(RenderTargetManager::ERROR_ID), ct1(renderTargetSettings, pixelSize, useMipmaps), ct2(renderTargetSettings, pixelSize, useMipmaps) { //TODO: Change to using a simple vector of the following struct instead of the "pass groups" thing. struct MiniEffect { public: PostProcessEffect* Effect; unsigned int Pass; MiniEffect(void) : Effect(0), Pass(0) { } MiniEffect(PostProcessEffect * effect, unsigned int pass = 1) : Effect(effect), Pass(pass) { assert(effect != 0 && Pass > 0); } bool IsBreak(void) const { return Effect == 0 || Pass == 0; } }; //First separate the effects into "pass groups" -- a chain of effects grouped by pass. //Multi-pass effects are each in their own group. //All passes don't need any kind of transformation for the vertices. std::vector<DataLine> vectorBuilder; vectorBuilder.insert(vectorBuilder.end(), DataLine(VertexInputNode::GetInstanceName(), 0)); vectorBuilder.insert(vectorBuilder.end(), DataLine(1.0f)); DataNode::Ptr combineToPos4(new CombineVectorNode(vectorBuilder, "ppeVertPosOut")); //Set up DataNode material data structures. DataNode::ClearMaterialData(); DataNode::VertexIns = DrawingQuad::GetAttributeData(); DataNode::MaterialOuts.VertexPosOutput = DataLine(combineToPos4->GetName()); //Build each pass group. std::vector<std::vector<PostProcessEffect::PpePtr>> passGroups; unsigned int passGroup = 0; totalPasses = 0; for (unsigned int effect = 0; effect < effectChain.size(); ++effect) { //If this is the start of a new pass, create the collection of effects for it. if (passGroup >= passGroups.size()) { totalPasses += 1; passGroups.insert(passGroups.end(), std::vector<PostProcessEffect::PpePtr>()); } //If this effect only has one pass, just put it in the current pass group. if (effectChain[effect]->NumbPasses == 1) { passGroups[passGroup].insert(passGroups[passGroup].end(), effectChain[effect]); } //Otherwise, create a new pass group just for this effect. else { totalPasses += effectChain[effect]->NumbPasses - 2; if (effect == effectChain.size() - 1) totalPasses += 1; if (passGroups[passGroup].size() > 0) { passGroup += 1; passGroups.insert(passGroups.end(), std::vector<std::shared_ptr<PostProcessEffect>>()); } passGroups[passGroup].insert(passGroups[passGroup].end(), effectChain[effect]); passGroup += 1; } } //Assemble each pass group into a material. for (passGroup = 0; passGroup < passGroups.size(); ++passGroup) { assert(passGroups[passGroup].size() > 0); DataNode::MaterialOuts.FragmentOutputs.clear(); DataNode::MaterialOuts.VertexOutputs.clear(); //If this is a multi-pass group, create the multiple passes. if (passGroups[passGroup][0]->NumbPasses > 1) { assert(passGroups[passGroup].size() == 1); PostProcessEffect::PpePtr effct = passGroups[passGroup][0]; effct->ChangePreviousEffect(); DataNode::MaterialOuts.FragmentOutputs.insert(DataNode::MaterialOuts.FragmentOutputs.end(), ShaderOutput("out_FinalColor", DataLine(effct->GetName(), PostProcessEffect::GetColorOutputIndex()))); //If there is a group before/after this, skip the first/last pass, since it will be lumped in with that other group. unsigned int startPass = 1, endPass = effct->NumbPasses; if (passGroup > 0) startPass += 1; if (passGroup < passGroups.size() - 1) endPass -= 1; for (unsigned int pass = startPass; pass <= endPass; ++pass) { effct->CurrentPass = pass; DataNode::MaterialOuts.VertexOutputs.clear(); DataNode::MaterialOuts.VertexOutputs.insert(DataNode::MaterialOuts.VertexOutputs.end(), ShaderOutput("fIn_UV", DataLine(VertexInputNode::GetInstanceName(), 1))); effct->OverrideVertexOutputs(DataNode::MaterialOuts.VertexOutputs); UniformDictionary unfs; ShaderGenerator::GeneratedMaterial genM = ShaderGenerator::GenerateMaterial(unfs, RenderingModes::RM_Opaque); if (!genM.ErrorMessage.empty()) { errorMsg = "Error generating shaders for pass #" + std::to_string(pass) + " of multi-pass effect '" + effct->GetName() + "': " + genM.ErrorMessage; return; } materials.insert(materials.end(), std::shared_ptr<Material>(genM.Mat)); params.AddUniforms(unfs, true); uniforms.insert(uniforms.end(), UniformDictionary()); if (materials[materials.size() - 1]->HasError()) { errorMsg = std::string() + "Error creating pass #" + std::to_string(pass) + " of multi-pass effect '" + effct->GetName() + "': " + materials[materials.size() - 1]->GetErrorMsg(); return; } } } //Otherwise, build up this pass using multiple effects. else { PostProcessEffect::PpePtr prev, current; //If there was a multi-pass effect before this group, put its final pass at the beginning of this group. if (passGroup > 0 && passGroups[passGroup - 1][0]->NumbPasses > 1) { prev = passGroups[passGroup - 1][0]; prev->CurrentPass = prev->NumbPasses; prev->ChangePreviousEffect(); } //Set each effect to happen on top of the previous. for (unsigned int effect = 0; effect < passGroups[passGroup].size(); ++effect) { current = passGroups[passGroup][effect]; current->CurrentPass = 1; current->ChangePreviousEffect(prev); prev = current; } assert(current.get() != 0); //If the next pass group is a multi-pass effect, put its first pass on the end of this group. if (passGroup < passGroups.size() - 1 && passGroups[passGroup + 1][0]->NumbPasses > 1) { current = passGroups[passGroup + 1][0]; current->CurrentPass = 1; current->ChangePreviousEffect(prev); prev = current; } //Now create the material. DataNode::MaterialOuts.VertexOutputs.clear(); DataNode::MaterialOuts.FragmentOutputs.clear(); DataNode::MaterialOuts.VertexOutputs.insert(DataNode::MaterialOuts.VertexOutputs.end(), ShaderOutput("fIn_UV", DataLine(VertexInputNode::GetInstanceName(), 1))); DataNode::MaterialOuts.FragmentOutputs.insert(DataNode::MaterialOuts.FragmentOutputs.end(), ShaderOutput("out_FinalColor", DataLine(current->GetName(), current->GetColorOutputIndex()))); current->OverrideVertexOutputs(DataNode::MaterialOuts.VertexOutputs); UniformDictionary unfs; ShaderGenerator::GeneratedMaterial genM = ShaderGenerator::GenerateMaterial(unfs, RenderingModes::RM_Opaque); if (!genM.ErrorMessage.empty()) { errorMsg = std::string() + "Error generating shaders for material #" + std::to_string(materials.size()) + ": " + genM.ErrorMessage; return; } materials.insert(materials.end(), std::shared_ptr<Material>(genM.Mat)); params.AddUniforms(unfs, true); uniforms.insert(uniforms.end(), UniformDictionary()); if (materials[materials.size() - 1]->HasError()) { errorMsg = std::string() + "Error creating material #" + std::to_string(materials.size()) + ": " + materials[materials.size() - 1]->GetErrorMsg(); return; } } } //Create needed render targets for rendering the post-process effect. if (materials.size() > 0) { ct1.Create(renderTargetSettings, useMipmaps, pixelSize); ct1.ClearData(screenWidth, screenHeight); rt1 = rtManager.CreateRenderTarget(PixelSizes::PS_16U_DEPTH); if (rt1 == RenderTargetManager::ERROR_ID) { errorMsg = "Error creating first render target: " + rtManager.GetError(); return; } rtManager[rt1]->SetColorAttachment(RenderTargetTex(&ct1), true); } if (materials.size() > 1) { ct2.Create(renderTargetSettings, useMipmaps, pixelSize); ct2.ClearData(screenWidth, screenHeight); rt2 = rtManager.CreateRenderTarget(PixelSizes::PS_16U_DEPTH); if (rt2 == RenderTargetManager::ERROR_ID) { errorMsg = "Error creating second render target: " + rtManager.GetError(); return; } rtManager[rt2]->SetColorAttachment(RenderTargetTex(&ct2), true); } }