Basic splatting.

This commit is contained in:
Andrzej Kapolka 2014-08-20 19:26:06 -07:00
parent 416d9bac2e
commit 72500630b0
6 changed files with 138 additions and 39 deletions

View file

@ -11,10 +11,18 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the diffuse texture
uniform sampler2D diffuseMap;
const int SPLAT_COUNT = 4;
// the splat textures
uniform sampler2D diffuseMaps[SPLAT_COUNT];
// alpha values for the four splat textures
varying vec4 alphaValues;
void main(void) {
// compute the base color based on OpenGL lighting model
gl_FragColor = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st);
// blend the splat textures
gl_FragColor = gl_Color * (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x +
texture2D(diffuseMaps[1], gl_TexCoord[0].st) * alphaValues.y +
texture2D(diffuseMaps[2], gl_TexCoord[0].st) * alphaValues.z +
texture2D(diffuseMaps[3], gl_TexCoord[0].st) * alphaValues.w);
}

View file

@ -14,20 +14,37 @@
// the height texture
uniform sampler2D heightMap;
// the texture that contains the texture indices
uniform sampler2D textureMap;
// the distance between height points in texture space
uniform float heightScale;
// the scale between height and texture textures
uniform float textureScale;
// the lower bounds of the values corresponding to the splat textures
uniform vec4 textureValueMinima;
// the upper bounds of the values corresponding to the splat textures
uniform vec4 textureValueMaxima;
// alpha values for the four splat textures
varying vec4 alphaValues;
void main(void) {
// add the height to the position
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
// the zero height should be invisible
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0 - step(height, 0.0));
// pass along the scaled/offset texture coordinates
gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * textureScale;
// compute the alpha values for each texture
float value = texture2D(textureMap, gl_TexCoord[0].st).r;
vec4 valueVector = vec4(value, value, value, value);
alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima);
}

View file

@ -673,13 +673,17 @@ public:
glm::vec3 vertex;
};
const int SPLAT_COUNT = 4;
const GLint SPLAT_TEXTURE_UNITS[] = { 3, 4, 5, 6 };
void HeightfieldBuffer::render(bool cursor) {
// initialize textures, etc. on first render
if (_heightTextureID == 0) {
glGenTextures(1, &_heightTextureID);
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _heightSize, _heightSize, 0,
@ -702,7 +706,8 @@ void HeightfieldBuffer::render(bool cursor) {
if (!_texture.isEmpty()) {
glGenTextures(1, &_textureTextureID);
glBindTexture(GL_TEXTURE_2D, _textureTextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
int textureSize = glm::sqrt(_texture.size());
@ -800,11 +805,53 @@ void HeightfieldBuffer::render(bool cursor) {
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
glDepthFunc(GL_LEQUAL);
glDepthMask(false);
glEnable(GL_BLEND);
glBlendFunc(GL_DST_COLOR, GL_ZERO);
glDisable(GL_ALPHA_TEST);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -1.0f);
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind();
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(
DefaultMetavoxelRendererImplementation::getSplatHeightScaleLocation(), 1.0f / _heightSize);
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(
DefaultMetavoxelRendererImplementation::getSplatTextureScaleLocation(), (float)_heightSize / innerSize);
glBindTexture(GL_TEXTURE_2D, _textureTextureID);
const int TEXTURES_PER_SPLAT = 4;
for (int i = 0; i < _textures.size(); i += TEXTURES_PER_SPLAT) {
for (int j = 0; j < SPLAT_COUNT; j++) {
glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]);
int index = i + j;
if (index < _networkTextures.size()) {
const NetworkTexturePointer& texture = _networkTextures.at(index);
glBindTexture(GL_TEXTURE_2D, texture ? texture->getID() : 0);
} else {
glBindTexture(GL_TEXTURE_2D, 0);
}
}
const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL;
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(
DefaultMetavoxelRendererImplementation::getSplatTextureValueMinimaLocation(),
(i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP,
(i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP);
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(
DefaultMetavoxelRendererImplementation::getSplatTextureValueMaximaLocation(),
(i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP,
(i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP);
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
}
glEnable(GL_ALPHA_TEST);
glBlendFunc(GL_DST_COLOR, GL_ZERO);
for (int i = 0; i < SPLAT_COUNT; i++) {
glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]);
glBindTexture(GL_TEXTURE_2D, 0);
}
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
if (Menu::getInstance()->isOptionChecked(MenuOption::SimpleShadows)) {
@ -831,7 +878,8 @@ void HeightfieldBuffer::render(bool cursor) {
glDisable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
glDepthFunc(GL_LESS);
glDepthMask(true);
glActiveTexture(GL_TEXTURE0);
} else {
@ -992,9 +1040,12 @@ void DefaultMetavoxelRendererImplementation::init() {
_splatHeightfieldProgram.bind();
_splatHeightfieldProgram.setUniformValue("heightMap", 0);
_splatHeightfieldProgram.setUniformValue("diffuseMap", 1);
_splatHeightfieldProgram.setUniformValue("textureMap", 1);
_splatHeightfieldProgram.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT);
_splatHeightScaleLocation = _splatHeightfieldProgram.uniformLocation("heightScale");
_splatTextureScaleLocation = _splatHeightfieldProgram.uniformLocation("textureScale");
_splatTextureValueMinimaLocation = _splatHeightfieldProgram.uniformLocation("textureValueMinima");
_splatTextureValueMaximaLocation = _splatHeightfieldProgram.uniformLocation("textureValueMaxima");
_splatHeightfieldProgram.release();
_lightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
@ -1638,6 +1689,8 @@ int DefaultMetavoxelRendererImplementation::_baseColorScaleLocation;
ProgramObject DefaultMetavoxelRendererImplementation::_splatHeightfieldProgram;
int DefaultMetavoxelRendererImplementation::_splatHeightScaleLocation;
int DefaultMetavoxelRendererImplementation::_splatTextureScaleLocation;
int DefaultMetavoxelRendererImplementation::_splatTextureValueMinimaLocation;
int DefaultMetavoxelRendererImplementation::_splatTextureValueMaximaLocation;
ProgramObject DefaultMetavoxelRendererImplementation::_lightHeightfieldProgram;
int DefaultMetavoxelRendererImplementation::_lightHeightScaleLocation;
ProgramObject DefaultMetavoxelRendererImplementation::_shadowLightHeightfieldProgram;

View file

@ -249,6 +249,8 @@ public:
static ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; }
static int getSplatHeightScaleLocation() { return _splatHeightScaleLocation; }
static int getSplatTextureScaleLocation() { return _splatTextureScaleLocation; }
static int getSplatTextureValueMinimaLocation() { return _splatTextureValueMinimaLocation; }
static int getSplatTextureValueMaximaLocation() { return _splatTextureValueMaximaLocation; }
static ProgramObject& getLightHeightfieldProgram() { return _lightHeightfieldProgram; }
static int getLightHeightScaleLocation() { return _lightHeightScaleLocation; }
@ -292,6 +294,8 @@ private:
static ProgramObject _splatHeightfieldProgram;
static int _splatHeightScaleLocation;
static int _splatTextureScaleLocation;
static int _splatTextureValueMinimaLocation;
static int _splatTextureValueMaximaLocation;
static ProgramObject _lightHeightfieldProgram;
static int _lightHeightScaleLocation;

View file

@ -932,6 +932,29 @@ void HeightfieldColorData::set(const QImage& image) {
memcpy(_contents.data(), image.constBits(), _contents.size());
}
const int TEXTURE_HEADER_SIZE = sizeof(qint32) * 4;
static QByteArray encodeTexture(int offsetX, int offsetY, int width, int height, const QByteArray& contents) {
QByteArray inflated(TEXTURE_HEADER_SIZE, 0);
qint32* header = (qint32*)inflated.data();
*header++ = offsetX;
*header++ = offsetY;
*header++ = width;
*header++ = height;
inflated.append(contents);
return qCompress(inflated);
}
static QByteArray decodeTexture(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) {
QByteArray inflated = qUncompress(encoded);
const qint32* header = (const qint32*)inflated.constData();
offsetX = *header++;
offsetY = *header++;
width = *header++;
height = *header++;
return inflated.mid(TEXTURE_HEADER_SIZE);
}
HeightfieldTextureData::HeightfieldTextureData(const QByteArray& contents, const QVector<SharedObjectPointer>& textures) :
HeightfieldData(contents),
_textures(textures) {
@ -951,32 +974,31 @@ HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes, const H
in.readDelta(_textures, reference->getTextures());
reference->setDeltaData(HeightfieldDataPointer(this));
_contents = reference->getContents();
QImage image = decodeHeightfieldImage(reference->getEncodedDelta());
if (image.isNull()) {
int offsetX, offsetY, width, height;
QByteArray delta = decodeTexture(reference->getEncodedDelta(), offsetX, offsetY, width, height);
if (delta.isEmpty()) {
return;
}
QPoint offset = image.offset();
if (offset.x() == 0) {
set(image);
if (offsetX == 0) {
_contents = delta;
return;
}
int minX = offset.x() - 1;
int minY = offset.y() - 1;
int minX = offsetX - 1;
int minY = offsetY - 1;
int size = glm::sqrt((float)_contents.size());
const char* src = delta.constData();
char* dest = _contents.data() + minY * size + minX;
for (int y = 0; y < image.height(); y++) {
memcpy(dest, image.constScanLine(y), image.width());
dest += size;
for (int y = 0; y < height; y++, src += width, dest += size) {
memcpy(dest, src, width);
}
}
void HeightfieldTextureData::write(Bitstream& out) {
QMutexLocker locker(&_encodedMutex);
if (_encoded.isEmpty()) {
QImage image;
int size = glm::sqrt((float)_contents.size());
image = QImage((uchar*)_contents.data(), size, size, QImage::Format_Indexed8);
_encoded = encodeHeightfieldImage(image, true);
_encoded = encodeTexture(0, 0, size, size, _contents);
}
out << _encoded.size();
out.writeAligned(_encoded);
@ -990,7 +1012,6 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
QImage image;
int size = glm::sqrt((float)_contents.size());
int minX = size, minY = size;
int maxX = -1, maxY = -1;
@ -1010,18 +1031,19 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture
maxY = qMax(maxY, y);
}
}
QByteArray delta;
int width = 0, height = 0;
if (maxX >= minX) {
int width = maxX - minX + 1;
int height = maxY - minY + 1;
image = QImage(width, height, QImage::Format_Indexed8);
width = maxX - minX + 1;
height = maxY - minY + 1;
delta = QByteArray(width * height, 0);
char* dest = delta.data();
src = _contents.constData() + minY * size + minX;
for (int y = 0; y < height; y++) {
memcpy(image.scanLine(y), src, width);
src += size;
for (int y = 0; y < height; y++, src += size, dest += width) {
memcpy(dest, src, width);
}
}
image.setOffset(QPoint(minX + 1, minY + 1));
reference->setEncodedDelta(encodeHeightfieldImage(image, true));
reference->setEncodedDelta(encodeTexture(minX + 1, minY + 1, width, height, delta));
reference->setDeltaData(HeightfieldDataPointer(this));
}
out << reference->getEncodedDelta().size();
@ -1030,15 +1052,11 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture
}
void HeightfieldTextureData::read(Bitstream& in, int bytes) {
set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)));
int offsetX, offsetY, width, height;
_contents = decodeTexture(_encoded = in.readAligned(bytes), offsetX, offsetY, width, height);
in >> _textures;
}
void HeightfieldTextureData::set(const QImage& image) {
_contents.resize(image.width() * image.height());
memcpy(_contents.data(), image.constBits(), _contents.size());
}
HeightfieldTexture::HeightfieldTexture() {
}

View file

@ -540,7 +540,6 @@ public:
private:
void read(Bitstream& in, int bytes);
void set(const QImage& image);
QVector<SharedObjectPointer> _textures;
};