mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 15:13:09 +02:00
Improved procedural surfaces, textures and more standard uniforms
This commit is contained in:
parent
dce4d94f30
commit
de5e95f7dc
7 changed files with 305 additions and 108 deletions
|
@ -56,7 +56,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
|
|||
|
||||
if (_procedural->ready()) {
|
||||
batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
|
||||
_procedural->prepare(batch, this->getDimensions());
|
||||
_procedural->prepare(batch, getPosition(), getDimensions());
|
||||
auto color = _procedural->getColor(cubeColor);
|
||||
batch._glColor4f(color.r, color.g, color.b, color.a);
|
||||
DependencyManager::get<GeometryCache>()->renderCube(batch);
|
||||
|
|
|
@ -62,7 +62,7 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
|
|||
modelTransform.postScale(SPHERE_ENTITY_SCALE);
|
||||
if (_procedural->ready()) {
|
||||
batch.setModelTransform(modelTransform); // use a transform with scale, rotation, registration point and translation
|
||||
_procedural->prepare(batch, getDimensions());
|
||||
_procedural->prepare(batch, getPosition(), getDimensions());
|
||||
auto color = _procedural->getColor(sphereColor);
|
||||
batch._glColor4f(color.r, color.g, color.b, color.a);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(batch);
|
||||
|
|
|
@ -246,6 +246,26 @@ public:
|
|||
void _glUniform4iv(int location, int count, const int* value);
|
||||
void _glUniformMatrix4fv(int location, int count, unsigned char transpose, const float* value);
|
||||
|
||||
void _glUniform(int location, int v0) {
|
||||
_glUniform1i(location, v0);
|
||||
}
|
||||
|
||||
void _glUniform(int location, float v0) {
|
||||
_glUniform1f(location, v0);
|
||||
}
|
||||
|
||||
void _glUniform(int location, const glm::vec2& v) {
|
||||
_glUniform2f(location, v.x, v.y);
|
||||
}
|
||||
|
||||
void _glUniform(int location, const glm::vec3& v) {
|
||||
_glUniform3f(location, v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
void _glUniform(int location, const glm::vec4& v) {
|
||||
_glUniform4f(location, v.x, v.y, v.z, v.w);
|
||||
}
|
||||
|
||||
void _glColor4f(float red, float green, float blue, float alpha);
|
||||
|
||||
enum Command {
|
||||
|
|
|
@ -17,20 +17,30 @@
|
|||
#include <gpu/Batch.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include "ProceduralShaders.h"
|
||||
|
||||
static const char* const UNIFORM_TIME_NAME= "iGlobalTime";
|
||||
static const char* const UNIFORM_SCALE_NAME = "iWorldScale";
|
||||
// Userdata parsing constants
|
||||
static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity";
|
||||
|
||||
static const QString URL_KEY = "shaderUrl";
|
||||
static const QString VERSION_KEY = "version";
|
||||
static const QString UNIFORMS_KEY = "uniforms";
|
||||
static const QString CHANNELS_KEY = "channels";
|
||||
|
||||
// Shader replace strings
|
||||
static const std::string PROCEDURAL_BLOCK = "//PROCEDURAL_BLOCK";
|
||||
static const std::string PROCEDURAL_COMMON_BLOCK = "//PROCEDURAL_COMMON_BLOCK";
|
||||
static const std::string PROCEDURAL_VERSION = "//PROCEDURAL_VERSION";
|
||||
|
||||
static const std::string STANDARD_UNIFORM_NAMES[Procedural::NUM_STANDARD_UNIFORMS] = {
|
||||
"iDate",
|
||||
"iGlobalTime",
|
||||
"iFrameCount",
|
||||
"iWorldScale",
|
||||
"iWorldPosition",
|
||||
"iChannelResolution"
|
||||
};
|
||||
|
||||
// Example
|
||||
//{
|
||||
|
@ -100,7 +110,21 @@ void Procedural::parse(const QJsonObject& proceduralData) {
|
|||
{
|
||||
auto uniforms = proceduralData[UNIFORMS_KEY];
|
||||
if (uniforms.isObject()) {
|
||||
_uniforms = uniforms.toObject();;
|
||||
_parsedUniforms = uniforms.toObject();
|
||||
}
|
||||
}
|
||||
|
||||
// Grab any textures
|
||||
{
|
||||
auto channels = proceduralData[CHANNELS_KEY];
|
||||
if (channels.isArray()) {
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
_parsedChannels = channels.toArray();
|
||||
size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)_parsedChannels.size());
|
||||
for (size_t i = 0; i < channelCount; ++i) {
|
||||
QString url = _parsedChannels.at(i).toString();
|
||||
_channels[i] = textureCache->getTexture(QUrl(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
_enabled = true;
|
||||
|
@ -111,20 +135,26 @@ bool Procedural::ready() {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!_shaderPath.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_networkShader) {
|
||||
return _networkShader->isLoaded();
|
||||
}
|
||||
|
||||
// Do we have a network or local shader
|
||||
if (_shaderPath.isEmpty() && (!_networkShader || !_networkShader->isLoaded())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do we have textures, and if so, are they loaded?
|
||||
for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {
|
||||
if (_channels[i] && !_channels[i]->isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Procedural::prepare(gpu::Batch& batch, const glm::vec3& size) {
|
||||
void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size) {
|
||||
_entityDimensions = size;
|
||||
_entityPosition = position;
|
||||
if (_shaderUrl.isLocalFile()) {
|
||||
auto lastModified = (quint64) QFileInfo(_shaderPath).lastModified().toMSecsSinceEpoch();
|
||||
auto lastModified = (quint64)QFileInfo(_shaderPath).lastModified().toMSecsSinceEpoch();
|
||||
if (lastModified > _shaderModified) {
|
||||
QFile file(_shaderPath);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
|
@ -164,69 +194,169 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& size) {
|
|||
//qDebug() << "FragmentShader:\n" << fragmentShaderSource.c_str();
|
||||
_fragmentShader = gpu::ShaderPointer(gpu::Shader::createPixel(fragmentShaderSource));
|
||||
_shader = gpu::ShaderPointer(gpu::Shader::createProgram(_vertexShader, _fragmentShader));
|
||||
gpu::Shader::makeProgram(*_shader);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("iChannel0"), 0));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("iChannel1"), 1));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("iChannel2"), 2));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("iChannel3"), 3));
|
||||
gpu::Shader::makeProgram(*_shader, slotBindings);
|
||||
|
||||
_pipeline = gpu::PipelinePointer(gpu::Pipeline::create(_shader, _state));
|
||||
_timeSlot = _shader->getUniforms().findLocation(UNIFORM_TIME_NAME);
|
||||
_scaleSlot = _shader->getUniforms().findLocation(UNIFORM_SCALE_NAME);
|
||||
for (size_t i = 0; i < NUM_STANDARD_UNIFORMS; ++i) {
|
||||
const std::string& name = STANDARD_UNIFORM_NAMES[i];
|
||||
_standardUniformSlots[i] = _shader->getUniforms().findLocation(name);
|
||||
}
|
||||
_start = usecTimestampNow();
|
||||
_frameCount = 0;
|
||||
}
|
||||
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
if (_pipelineDirty) {
|
||||
_pipelineDirty = false;
|
||||
setupUniforms();
|
||||
}
|
||||
|
||||
|
||||
for (auto lambda : _uniforms) {
|
||||
lambda(batch);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {
|
||||
if (_channels[i] && _channels[i]->isLoaded()) {
|
||||
batch.setResourceTexture(i, _channels[i]->getGPUTexture());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Procedural::setupUniforms() {
|
||||
_uniforms.clear();
|
||||
// Set any userdata specified uniforms
|
||||
foreach(QString key, _uniforms.keys()) {
|
||||
foreach(QString key, _parsedUniforms.keys()) {
|
||||
std::string uniformName = key.toLocal8Bit().data();
|
||||
int32_t slot = _shader->getUniforms().findLocation(uniformName);
|
||||
if (gpu::Shader::INVALID_LOCATION == slot) {
|
||||
continue;
|
||||
}
|
||||
QJsonValue value = _uniforms[key];
|
||||
QJsonValue value = _parsedUniforms[key];
|
||||
if (value.isDouble()) {
|
||||
batch._glUniform1f(slot, value.toDouble());
|
||||
float v = value.toDouble();
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform1f(slot, v);
|
||||
});
|
||||
} else if (value.isArray()) {
|
||||
auto valueArray = value.toArray();
|
||||
switch (valueArray.size()) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
batch._glUniform1f(slot, valueArray[0].toDouble());
|
||||
case 1: {
|
||||
float v = valueArray[0].toDouble();
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform1f(slot, v);
|
||||
});
|
||||
break;
|
||||
case 2:
|
||||
batch._glUniform2f(slot,
|
||||
valueArray[0].toDouble(),
|
||||
valueArray[1].toDouble());
|
||||
}
|
||||
case 2: {
|
||||
glm::vec2 v{ valueArray[0].toDouble(), valueArray[1].toDouble() };
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform2f(slot, v.x, v.y);
|
||||
});
|
||||
break;
|
||||
case 3:
|
||||
batch._glUniform3f(slot,
|
||||
valueArray[0].toDouble(),
|
||||
valueArray[1].toDouble(),
|
||||
valueArray[2].toDouble());
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
batch._glUniform4f(slot,
|
||||
}
|
||||
case 3: {
|
||||
glm::vec3 v{
|
||||
valueArray[0].toDouble(),
|
||||
valueArray[1].toDouble(),
|
||||
valueArray[2].toDouble(),
|
||||
valueArray[3].toDouble());
|
||||
};
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform3f(slot, v.x, v.y, v.z);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
case 4: {
|
||||
glm::vec4 v{
|
||||
valueArray[0].toDouble(),
|
||||
valueArray[1].toDouble(),
|
||||
valueArray[2].toDouble(),
|
||||
valueArray[3].toDouble(),
|
||||
};
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform4f(slot, v.x, v.y, v.z, v.w);
|
||||
});
|
||||
break;
|
||||
|
||||
}
|
||||
valueArray.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[TIME]) {
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
// Minimize floating point error by doing an integer division to milliseconds, before the floating point division to seconds
|
||||
float time = (float)((usecTimestampNow() - _start) / USECS_PER_MSEC) / MSECS_PER_SECOND;
|
||||
batch._glUniform1f(_timeSlot, time);
|
||||
// FIXME move into the 'set once' section, since this doesn't change over time
|
||||
batch._glUniform3f(_scaleSlot, size.x, size.y, size.z);
|
||||
}
|
||||
batch._glUniform(_standardUniformSlots[TIME], time);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[DATE]) {
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
QDateTime now = QDateTime::currentDateTimeUtc();
|
||||
QDate date = now.date();
|
||||
QTime time = now.time();
|
||||
vec4 v;
|
||||
v.x = date.year();
|
||||
// Shadertoy month is 0 based
|
||||
v.y = date.month() - 1;
|
||||
// But not the day... go figure
|
||||
v.z = date.day();
|
||||
v.w = (time.hour() * 3600) + (time.minute() * 60) + time.second();
|
||||
batch._glUniform(_standardUniformSlots[DATE], v);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[FRAME_COUNT]) {
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform(_standardUniformSlots[FRAME_COUNT], ++_frameCount);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[SCALE]) {
|
||||
// FIXME move into the 'set once' section, since this doesn't change over time
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform(_standardUniformSlots[SCALE], _entityDimensions);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[SCALE]) {
|
||||
// FIXME move into the 'set once' section, since this doesn't change over time
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform(_standardUniformSlots[SCALE], _entityDimensions);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[POSITION]) {
|
||||
// FIXME move into the 'set once' section, since this doesn't change over time
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform(_standardUniformSlots[POSITION], _entityPosition);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[CHANNEL_RESOLUTION]) {
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
vec3 channelSizes[MAX_PROCEDURAL_TEXTURE_CHANNELS];
|
||||
for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {
|
||||
if (_channels[i]) {
|
||||
channelSizes[i] = vec3(_channels[i]->getWidth(), _channels[i]->getHeight(), 1.0);
|
||||
}
|
||||
}
|
||||
batch._glUniform3fv(_standardUniformSlots[CHANNEL_RESOLUTION], MAX_PROCEDURAL_TEXTURE_CHANNELS, &channelSizes[0].x);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec4 Procedural::getColor(const glm::vec4& entityColor) {
|
||||
if (_version == 1) {
|
||||
|
|
|
@ -14,11 +14,16 @@
|
|||
#include <QtCore/QString>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonArray>
|
||||
|
||||
#include <gpu/Shader.h>
|
||||
#include <gpu/Pipeline.h>
|
||||
#include <gpu/Batch.h>
|
||||
#include <model-networking/ShaderCache.h>
|
||||
#include <model-networking/TextureCache.h>
|
||||
|
||||
using UniformLambdas = std::list<std::function<void(gpu::Batch& batch)>>;
|
||||
const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
|
||||
|
||||
// FIXME better encapsulation
|
||||
// FIXME better mechanism for extending to things rendered using shaders other than simple.slv
|
||||
|
@ -29,7 +34,8 @@ struct Procedural {
|
|||
void parse(const QString& userDataJson);
|
||||
void parse(const QJsonObject&);
|
||||
bool ready();
|
||||
void prepare(gpu::Batch& batch, const glm::vec3& size);
|
||||
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size);
|
||||
void setupUniforms();
|
||||
glm::vec4 getColor(const glm::vec4& entityColor);
|
||||
|
||||
bool _enabled{ false };
|
||||
|
@ -43,17 +49,34 @@ struct Procedural {
|
|||
QUrl _shaderUrl;
|
||||
quint64 _shaderModified{ 0 };
|
||||
bool _pipelineDirty{ true };
|
||||
int32_t _timeSlot{ gpu::Shader::INVALID_LOCATION };
|
||||
int32_t _scaleSlot{ gpu::Shader::INVALID_LOCATION };
|
||||
uint64_t _start{ 0 };
|
||||
NetworkShaderPointer _networkShader;
|
||||
QJsonObject _uniforms;
|
||||
|
||||
enum StandardUniforms {
|
||||
DATE,
|
||||
TIME,
|
||||
FRAME_COUNT,
|
||||
SCALE,
|
||||
POSITION,
|
||||
CHANNEL_RESOLUTION,
|
||||
NUM_STANDARD_UNIFORMS
|
||||
};
|
||||
|
||||
int32_t _standardUniformSlots[NUM_STANDARD_UNIFORMS];
|
||||
|
||||
uint64_t _start{ 0 };
|
||||
int32_t _frameCount{ 0 };
|
||||
NetworkShaderPointer _networkShader;
|
||||
QJsonObject _parsedUniforms;
|
||||
QJsonArray _parsedChannels;
|
||||
|
||||
UniformLambdas _uniforms;
|
||||
NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
|
||||
gpu::PipelinePointer _pipeline;
|
||||
gpu::ShaderPointer _vertexShader;
|
||||
gpu::ShaderPointer _fragmentShader;
|
||||
gpu::ShaderPointer _shader;
|
||||
gpu::StatePointer _state;
|
||||
glm::vec3 _entityDimensions;
|
||||
glm::vec3 _entityPosition;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -262,15 +262,39 @@ float snoise(vec2 v) {
|
|||
return 130.0 * dot(m, g);
|
||||
}
|
||||
|
||||
// TODO add more uniforms
|
||||
uniform float iGlobalTime; // shader playback time (in seconds)
|
||||
uniform vec3 iWorldScale; // the dimensions of the object being rendered
|
||||
|
||||
// TODO add support for textures
|
||||
// TODO document available inputs other than the uniforms
|
||||
// TODO provide world scale in addition to the untransformed position
|
||||
// shader playback time (in seconds)
|
||||
uniform float iGlobalTime;
|
||||
// the dimensions of the object being rendered
|
||||
uniform vec3 iWorldScale;
|
||||
|
||||
#define PROCEDURAL 1
|
||||
|
||||
//PROCEDURAL_VERSION
|
||||
|
||||
#ifdef PROCEDURAL_V1
|
||||
|
||||
#else
|
||||
|
||||
// Unimplemented uniforms
|
||||
// Resolution doesn't make sense in the VR context
|
||||
const vec3 iResolution = vec3(1.0);
|
||||
// Mouse functions not enabled currently
|
||||
const vec4 iMouse = vec4(0.0);
|
||||
// No support for audio input
|
||||
const float iSampleRate = 1.0;
|
||||
// No support for video input
|
||||
const vec4 iChannelTime = vec4(0.0);
|
||||
|
||||
|
||||
uniform vec4 iDate;
|
||||
uniform int iFrameCount;
|
||||
uniform vec3 iWorldPosition;
|
||||
uniform vec3 iChannelResolution[4];
|
||||
uniform sampler2D iChannel0;
|
||||
uniform sampler2D iChannel1;
|
||||
uniform sampler2D iChannel2;
|
||||
uniform sampler2D iChannel3;
|
||||
|
||||
#endif
|
||||
|
||||
)SHADER";
|
||||
|
|
|
@ -74,7 +74,7 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum,
|
|||
batch.setResourceTexture(0, skybox.getCubemap());
|
||||
}
|
||||
|
||||
skybox._procedural->prepare(batch, glm::vec3(1));
|
||||
skybox._procedural->prepare(batch, glm::vec3(0), glm::vec3(1));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue