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 + -
显示快捷键?