terraintextureglsl.cpp
来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 915 行 · 第 1/2 页
CPP
915 行
/*
---------------------------------------------------------------------
Terrain Renderer using texture splatting and geomipmapping
Copyright (c) 2006 Jelmer Cnossen
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
Jelmer Cnossen
j.cnossen at gmail dot com
---------------------------------------------------------------------
*/
#include "StdAfx.h"
#include "TerrainBase.h"
#include "TerrainTexture.h"
#include "Terrain.h"
#include "TerrainTextureGLSL.h"
#include "Rendering/GL/IFramebuffer.h"
#include "FileSystem/FileHandler.h"
#include "Platform/FileSystem.h"
#include "bitops.h"
#include <fstream>
namespace terrain {
using namespace std;
static void ShowInfoLog(GLhandleARB handle)
{
d_trace("Shader failed to compile, showing info log:\n");
int infoLogLen, actualLength;
glGetObjectParameterivARB(handle, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infoLogLen);
char *infoLog = new char[infoLogLen];
glGetInfoLogARB(handle, infoLogLen, &actualLength, infoLog);
d_puts(infoLog);
delete[] infoLog;
}
struct Shader
{
list<string> texts;
GLhandleARB handle;
Shader() { handle = 0; }
void AddFile(const std::string& file)
{
CFileHandler fh(file);
if (!fh.FileExists())
throw content_error("Can't load shader " + file);
string text;
text.resize(fh.FileSize());
fh.Read(&text[0], text.length());
texts.push_back(text);
}
void Build(GLenum shaderType)
{
handle = glCreateShaderObjectARB(shaderType);
vector<int> lengths(texts.size());
vector<const GLcharARB*> strings(texts.size());
int index=0;
for (list<string>::iterator i=texts.begin(); i != texts.end(); ++i, index++) {
lengths[index] = i->length();
strings[index] = i->c_str();
}
// if (shaderType == GL_FRAGMENT_SHADER_ARB)
// DebugOutput(shaderType);
glShaderSourceARB(handle, strings.size(), &strings.front(), &lengths.front());
glCompileShaderARB(handle);
// Check compile status and show info log if failed
int isCompiled;
glGetObjectParameterivARB(handle, GL_OBJECT_COMPILE_STATUS_ARB, &isCompiled);
if (!isCompiled)
{
WriteToFile("sm3_failed_shader.glsl");
ShowInfoLog(handle);
string errMsg = "Failed to build ";
throw std::runtime_error (errMsg + (shaderType == GL_VERTEX_SHADER_ARB ? "vertex shader" : "fragment shader"));
}
}
void DebugOutput(GLenum shaderType)
{
char fn[20];
static int fpc=0;
static int vpc=0;
if (shaderType == GL_FRAGMENT_SHADER_ARB) sprintf (fn, "shader%dfp.txt", fpc++);
else sprintf (fn, "shader%dvp.txt", vpc++);
WriteToFile(fn);
}
void WriteToFile(const char *fn)
{
std::string n = filesystem.LocateFile(fn, FileSystem::WRITE);
FILE *f = fopen(n.c_str(), "w");
if(f) {
for (list<string>::iterator i=texts.begin();i!=texts.end();++i)
fputs(i->c_str(), f);
fclose(f);
}
}
};
static int closest_pot(int i)
{
int next = next_power_of_2(i);
return (next - i < i - next/2) ? next : next/2;
}
// A framebuffer enabled as texture
class BufferTexture : public BaseTexture
{
public:
BufferTexture()
{
// ATI has GL_EXT_texture_rectangle, but that has no support for GLSL texture2DRect
// nVidia: Use RECT, ati: use POT
width = gu->viewSizeX;
height = gu->viewSizeY;
if (GLEW_ARB_texture_rectangle)
target = GL_TEXTURE_RECTANGLE_ARB;
else {
target = GL_TEXTURE_2D;
width = closest_pot(width);
height = closest_pot(height);
}
framebuffer = instantiate_fb(width, height, FBO_NEED_COLOR | FBO_NEED_DEPTH);
name = "_buffer";
glGenTextures(1, &id);
glBindTexture(target, id);
glTexParameteri(target,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(target,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexImage2D(target, 0, 4, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
framebuffer->attachTexture(id, target, FBO_ATTACH_COLOR);
assert (framebuffer->valid());
}
~BufferTexture()
{
delete framebuffer;
// texture is deleted by ~BaseTexture
}
bool IsRect() { return target == GL_TEXTURE_RECTANGLE_ARB; }
int width, height;
uint target;
IFramebuffer* framebuffer;
};
struct ShaderBuilder
{
RenderSetup *renderSetup;
TextureUsage texUsage;
BufferTexture* buffer;
bool ShadowMapping () { return renderSetup->shaderDef.useShadowMapping; }
Shader lastFragmentShader, lastVertexShader; // for debugging
ShaderBuilder(RenderSetup *rs);
std::string GenTextureRead (int tu, int tc);
NodeGLSLShader* EndPass(ShaderDef* sd, const std::string &operations, uint passIndex=0);
void BuildFragmentShader(NodeGLSLShader *ns, uint passIndex, const std::string& operations, ShaderDef* sd);
void BuildVertexShader(NodeGLSLShader *ns, uint passIndex, ShaderDef *sd);
bool ProcessStage(vector<ShaderDef::Stage>& stages, uint &index, std::string& opstr);
void Build(ShaderDef* shaderDef);
void AddPPDefines(ShaderDef *sd, Shader& shader, uint passIndex);
enum ShadingMethod
{
SM_DiffuseSP, // lit diffuse single pass
SM_DiffuseBumpmapSP, // full diffuse + bumpmapping in single pass
SM_DiffuseBumpmapMP, // diffuse pass + bumpmap pass
SM_Impossible // massive failure
};
ShadingMethod shadingMethod;
ShadingMethod CalculateShadingMethod(ShaderDef *sd) const;
struct TexReq
{
TexReq() { coords=units=0; }
int coords, units;
void GetFromGL();
bool Fits(TexReq maxrq) {
return coords <= maxrq.coords && units <= maxrq.units;
}
TexReq operator+(const TexReq& rq) {
TexReq r;
r.coords = coords+rq.coords;
r.units = units+rq.units;
return r;
}
};
// Calculate the texturing requirements for the specified stages
TexReq CalcStagesTexReq (const vector<ShaderDef::Stage>& stages, uint startIndex) const;
};
// Decide how to organise the shading, ie: in how many passes
ShaderBuilder::ShadingMethod ShaderBuilder::CalculateShadingMethod(ShaderDef *sd) const
{
TexReq diffuseRQ = CalcStagesTexReq(sd->stages, 0);
TexReq bumpmapRQ;
TexReq hwmax;
hwmax.GetFromGL();
if (!sd->normalMapStages.empty())
bumpmapRQ = CalcStagesTexReq(sd->normalMapStages, 0);
TexReq special;
if (sd->useShadowMapping)
{
// add shadow buffer + shadow texture coord
special.coords ++;
special.units ++;
}
if (sd->normalMapStages.empty())
{
if ( (diffuseRQ + special).Fits (hwmax) )
return SM_DiffuseSP;
else
return SM_Impossible;
}
special.coords += 2; // lightdir + tsEyeDir or lightdir + wsEyeDir
TexReq total = special + bumpmapRQ + diffuseRQ;
// diffuse + bumpmap in one pass?
if (total.Fits (hwmax))
return SM_DiffuseBumpmapSP;
// for multipass, one extra texture read is required for the diffuse input
special.units ++;
// is multipass possible?
if (diffuseRQ.Fits (hwmax) && (bumpmapRQ + special).Fits (hwmax))
return SM_DiffuseBumpmapMP;
// no options left
return SM_Impossible;
}
ShaderBuilder::TexReq ShaderBuilder::CalcStagesTexReq (const vector<ShaderDef::Stage>& stages, uint index) const
{
TextureUsage usage;
while(index < stages.size())
{
const ShaderDef::Stage& stage = stages[index];
BaseTexture *texture = stage.source;
TextureUsage tmpUsage;
usage.AddTextureRead (-1, texture);
usage.AddTextureCoordRead (-1, texture);
if(stage.operation == ShaderDef::Alpha) {
// next operation is blend (alpha is autoinserted before blend)
assert (index < stages.size()-1 && stages[index+1].operation == ShaderDef::Blend);
const ShaderDef::Stage& blendStage = stages[index+1];
usage.AddTextureRead(-1, blendStage.source);
usage.AddTextureCoordRead(-1, blendStage.source);
index++;
}
index++;
}
TexReq rq;
rq.coords = usage.coordUnits.size();
rq.units = usage.texUnits.size();
return rq;
}
void ShaderBuilder::TexReq::GetFromGL()
{
glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &units);
glGetIntegerv (GL_MAX_TEXTURE_COORDS_ARB, &coords);
}
ShaderBuilder::ShaderBuilder(RenderSetup *rs) :
renderSetup(rs)
{
buffer = 0;
}
std::string ShaderBuilder::GenTextureRead (int tu, int tc)
{
char tcstr[6];
sprintf (tcstr,"%d", tc);
return "texture2D(" + texUsage.texUnits[tu]->name + ", gl_TexCoord[" + tcstr + "].st)";
}
NodeGLSLShader* ShaderBuilder::EndPass(ShaderDef* sd, const std::string &operations, uint passIndex)
{
NodeGLSLShader* nodeShader = new NodeGLSLShader;
if (shadingMethod == SM_DiffuseSP)
nodeShader->vertBufReq = VRT_Normal;
else
nodeShader->vertBufReq = VRT_TangentSpaceMatrix;
//nodeShader->vertBufReq = VRT_Normal | VRT_TangentSpaceMatrix;
nodeShader->texCoordGen = texUsage.coordUnits;
nodeShader->texUnits = texUsage.texUnits;
BuildVertexShader(nodeShader, passIndex, sd);
BuildFragmentShader(nodeShader, passIndex, operations, sd);
nodeShader->program = glCreateProgramObjectARB();
glAttachObjectARB(nodeShader->program, nodeShader->vertexShader);
glAttachObjectARB(nodeShader->program, nodeShader->fragmentShader);
glLinkProgramARB(nodeShader->program);
int isLinked;
glGetObjectParameterivARB(nodeShader->program, GL_OBJECT_LINK_STATUS_ARB, &isLinked);
if (!isLinked)
{
d_trace ("Failed to link shaders. Showing info log:\n");
lastFragmentShader.WriteToFile("sm3_fragmentshader.txt");
lastVertexShader.WriteToFile("sm3_vertexshader.txt");
ShowInfoLog (nodeShader->program);
throw std::runtime_error("Failed to link shaders");
}
glUseProgramObjectARB(nodeShader->program);
// set texture image units to texture samplers in the shader
for (int a=0;a<nodeShader->texUnits.size();a++)
{
BaseTexture *tex = nodeShader->texUnits[a];
GLint location = glGetUniformLocationARB(nodeShader->program, tex->name.c_str());
glUniform1iARB(location, a);
}
// have bumpmapping?
if (shadingMethod != SM_DiffuseSP &&
!(shadingMethod == SM_DiffuseBumpmapMP && passIndex==0))
{
nodeShader->tsmAttrib = glGetAttribLocationARB(nodeShader->program,"TangentSpaceMatrix");
nodeShader->wsLightDirLocation = glGetUniformLocationARB(nodeShader->program, "wsLightDir");
nodeShader->wsEyePosLocation = glGetUniformLocationARB(nodeShader->program, "wsEyePos");
}
if (ShadowMapping())
{
nodeShader->shadowMapLocation = glGetUniformLocationARB(nodeShader->program, "shadowMap");
nodeShader->shadowParamsLocation = glGetUniformLocationARB(nodeShader->program, "shadowParams");
nodeShader->shadowMatrixLocation = glGetUniformLocationARB(nodeShader->program, "shadowMatrix");
}
if (passIndex == 1)
{
// set up uniform to read bumpmap
GLint invScreenDim = glGetUniformLocationARB(nodeShader->program, "invScreenDim");
glUniform2fARB(invScreenDim, 1.0f/gu->viewSizeX, 1.0f/gu->viewSizeY);
}
glUseProgramObjectARB(0);
renderSetup->passes.push_back (RenderPass());
RenderPass& rp = renderSetup->passes.back();
rp.shaderSetup = nodeShader;
rp.operation = Pass_Replace;
rp.depthWrite = true;
nodeShader->debugstr = operations;
NodeGLSLShader *ns = nodeShader;
nodeShader = 0;
texUsage = TextureUsage();
return ns;
}
void ShaderBuilder::AddPPDefines(ShaderDef *sd, Shader& shader, uint passIndex)
{
bool bumpmapping = (shadingMethod != SM_DiffuseSP &&
!(shadingMethod == SM_DiffuseBumpmapMP && passIndex==0));
if (bumpmapping)
{
shader.texts.push_back("#define UseBumpMapping\n");
if (passIndex == 1) {
shader.texts.push_back("#define DiffuseFromBuffer\n");
if (GLEW_ARB_texture_rectangle) shader.texts.push_back("#define UseTextureRECT\n");
}
}
if (ShadowMapping())
shader.texts.push_back ("#define UseShadowMapping\n");
shader.AddFile("shaders/terrainCommon.glsl");
char specularExponentStr[20];
SNPRINTF(specularExponentStr, 20, "%5.3f", sd->specularExponent);
shader.texts.push_back(string("const float specularExponent = ") + specularExponentStr + ";\n");
}
void ShaderBuilder::BuildFragmentShader(NodeGLSLShader *ns, uint passIndex, const std::string& operations, ShaderDef* sd)
{
lastFragmentShader = Shader();
Shader& fragmentShader = lastFragmentShader;
// insert texture samplers
string textureSamplers;
for (int a=0;a<ns->texUnits.size();a++)
{
BaseTexture *tex = ns->texUnits[a];
if (tex->IsRect())
textureSamplers += "uniform sampler2DRect " + tex->name + ";\n";
else
textureSamplers += "uniform sampler2D " + tex->name + ";\n";
}
AddPPDefines(sd, fragmentShader, passIndex);
fragmentShader.texts.push_back(textureSamplers);
fragmentShader.AddFile("shaders/terrainFragmentShader.glsl");
string gentxt = "vec4 CalculateColor() { vec4 color; float curalpha; \n" + operations;
switch (shadingMethod)
{
case SM_DiffuseSP:
gentxt += "return Light(color); }\n";
break;
case SM_DiffuseBumpmapSP:
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?