mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-14 08:16:03 +02:00
Merge pull request #5727 from jherico/marge
Procedural rendering for box & sphere entities
This commit is contained in:
commit
1df3a8f814
13 changed files with 833 additions and 14 deletions
102
examples/shaders/grid.fs
Normal file
102
examples/shaders/grid.fs
Normal file
|
@ -0,0 +1,102 @@
|
|||
#line 2
|
||||
|
||||
// https://www.shadertoy.com/view/lss3WS
|
||||
|
||||
// srtuss, 2013
|
||||
|
||||
// collecting some design ideas for a new game project.
|
||||
// no raymarching is used.
|
||||
|
||||
// if i could add a custom soundtrack, it'd use this one (essential for desired sensation)
|
||||
// http://www.youtube.com/watch?v=1uFAu65tZpo
|
||||
|
||||
//#define GREEN_VERSION
|
||||
|
||||
// ** improved camera shaking
|
||||
// ** cleaned up code
|
||||
// ** added stuff to the gates
|
||||
|
||||
// srtuss, 2013
|
||||
float time = iGlobalTime;
|
||||
|
||||
// some noise functions for fast developing
|
||||
float rand11(float p) {
|
||||
return fract(sin(p * 591.32) * 43758.5357);
|
||||
}
|
||||
|
||||
float rand12(vec2 p) {
|
||||
return fract(sin(dot(p.xy, vec2(12.9898, 78.233))) * 43758.5357);
|
||||
}
|
||||
|
||||
vec2 rand21(float p) {
|
||||
return fract(vec2(sin(p * 591.32), cos(p * 391.32)));
|
||||
}
|
||||
|
||||
vec2 rand22(in vec2 p)
|
||||
{
|
||||
return fract(vec2(sin(p.x * 591.32 + p.y * 154.077),
|
||||
cos(p.x * 391.32 + p.y * 49.077)));
|
||||
}
|
||||
|
||||
vec3 voronoi(in vec2 x) {
|
||||
vec2 n = floor(x); // grid cell id
|
||||
vec2 f = fract(x);// grid internal position
|
||||
vec2 mg;// shortest distance...
|
||||
vec2 mr;// ..and second shortest distance
|
||||
float md = 8.0, md2 = 8.0;
|
||||
for(int j = -1; j <= 1; j ++)
|
||||
{
|
||||
for(int i = -1; i <= 1; i ++)
|
||||
{
|
||||
vec2 g = vec2(float(i), float(j)); // cell id
|
||||
vec2 o = rand22(n + g);// offset to edge point
|
||||
vec2 r = g + o - f;
|
||||
|
||||
float d = max(abs(r.x), abs(r.y));// distance to the edge
|
||||
|
||||
if(d < md)
|
||||
{ md2 = md; md = d; mr = r; mg = g;}
|
||||
else if(d < md2)
|
||||
{ md2 = d;}
|
||||
}
|
||||
}
|
||||
return vec3(n + mg, md2 - md);
|
||||
}
|
||||
|
||||
|
||||
vec4 getProceduralColor() {
|
||||
float inten = 0.0;
|
||||
vec3 its;
|
||||
float v, g;
|
||||
// voronoi floor layers
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float layer = float(i);
|
||||
vec2 pos = _position.xz * 100.0;
|
||||
if (i == 2) {
|
||||
pos.x += time * 0.05;
|
||||
} else if (i == 1) {
|
||||
pos.y += time * 0.07;
|
||||
}
|
||||
vec3 vo = voronoi(pos + 8.0 * rand21(float(i)));
|
||||
|
||||
v = exp(-100.0 * (vo.z - 0.02));
|
||||
float fx = 0.0;
|
||||
|
||||
// add some special fx to lowest layer
|
||||
if (i == 3) {
|
||||
float crd = 0.0; //fract(time * 0.2) * 50.0 - 25.0;
|
||||
float fxi = cos(vo.x * 0.2 + time * 0.5); //abs(crd - vo.x);
|
||||
fx = clamp(smoothstep(0.9, 1.0, fxi), 0.0, 0.9) * 1.0 * rand12(vo.xy);
|
||||
fx *= exp(-3.0 * vo.z) * 2.0;
|
||||
}
|
||||
inten += v * 0.1 + fx;
|
||||
}
|
||||
inten *= 0.4 + (sin(time) * 0.5 + 0.5) * 0.6;
|
||||
vec3 cr = vec3(0.15, 2.0, 9.0);
|
||||
vec3 cg = vec3(2.0, 0.15, 9.0);
|
||||
vec3 cb = vec3(9.0, 2.0, 0.15);
|
||||
vec3 ct = vec3(9.0, 0.25, 0.3);
|
||||
vec3 cy = vec3(0.25, 0.3, 9.3);
|
||||
vec3 col = pow(vec3(inten), 1.5 * cy);
|
||||
return vec4(col, 1.0);
|
||||
}
|
48
examples/shaders/hex.fs
Normal file
48
examples/shaders/hex.fs
Normal file
|
@ -0,0 +1,48 @@
|
|||
#line 2
|
||||
// Created by inigo quilez - iq/2014
|
||||
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
|
||||
|
||||
// { 2d cell id, distance to border, distnace to center )
|
||||
vec4 hexagon(vec2 p) {
|
||||
vec2 q = vec2(p.x * 2.0 * 0.5773503, p.y + p.x * 0.5773503);
|
||||
|
||||
vec2 pi = floor(q);
|
||||
vec2 pf = fract(q);
|
||||
|
||||
float v = mod(pi.x + pi.y, 3.0);
|
||||
|
||||
float ca = step(1.0, v);
|
||||
float cb = step(2.0, v);
|
||||
vec2 ma = step(pf.xy, pf.yx);
|
||||
|
||||
// distance to borders
|
||||
float e = dot(ma,
|
||||
1.0 - pf.yx + ca * (pf.x + pf.y - 1.0) + cb * (pf.yx - 2.0 * pf.xy));
|
||||
|
||||
// distance to center
|
||||
p = vec2(q.x + floor(0.5 + p.y / 1.5), 4.0 * p.y / 3.0) * 0.5 + 0.5;
|
||||
float f = length((fract(p) - 0.5) * vec2(1.0, 0.85));
|
||||
|
||||
return vec4(pi + ca - cb * ma, e, f);
|
||||
}
|
||||
|
||||
|
||||
float hash1(vec2 p) {
|
||||
float n = dot(p, vec2(127.1, 311.7));
|
||||
return fract(sin(n) * 43758.5453);
|
||||
}
|
||||
|
||||
vec4 getProceduralColor() {
|
||||
vec2 uv = _position.xz + 0.5;
|
||||
vec2 pos = _position.xz * iWorldScale.xz;
|
||||
// gray
|
||||
vec4 h = hexagon(8.0 * pos + 0.5);
|
||||
float n = snoise(vec3(0.3 * h.xy + iGlobalTime * 0.1, iGlobalTime));
|
||||
vec3 col = 0.15 + 0.15 * hash1(h.xy + 1.2) * vec3(1.0);
|
||||
col *= smoothstep(0.10, 0.11, h.z);
|
||||
col *= smoothstep(0.10, 0.11, h.w);
|
||||
col *= 1.0 + 0.15 * sin(40.0 * h.z);
|
||||
col *= 0.75 + 0.5 * h.z * n;
|
||||
col *= pow(16.0 * uv.x * (1.0 - uv.x) * uv.y * (1.0 - uv.y), 0.1);
|
||||
return vec4(col, 1.0);
|
||||
}
|
48
examples/shaders/test.fs
Normal file
48
examples/shaders/test.fs
Normal file
|
@ -0,0 +1,48 @@
|
|||
#line 2
|
||||
// Created by inigo quilez - iq/2014
|
||||
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
|
||||
|
||||
// { 2d cell id, distance to border, distnace to center )
|
||||
vec4 hexagon(vec2 p) {
|
||||
vec2 q = vec2(p.x * 2.0 * 0.5773503, p.y + p.x * 0.5773503);
|
||||
|
||||
vec2 pi = floor(q);
|
||||
vec2 pf = fract(q);
|
||||
|
||||
float v = mod(pi.x + pi.y, 3.0);
|
||||
|
||||
float ca = step(1.0, v);
|
||||
float cb = step(2.0, v);
|
||||
vec2 ma = step(pf.xy, pf.yx);
|
||||
|
||||
// distance to borders
|
||||
float e = dot(ma,
|
||||
1.0 - pf.yx + ca * (pf.x + pf.y - 1.0) + cb * (pf.yx - 2.0 * pf.xy));
|
||||
|
||||
// distance to center
|
||||
p = vec2(q.x + floor(0.5 + p.y / 1.5), 4.0 * p.y / 3.0) * 0.5 + 0.5;
|
||||
float f = length((fract(p) - 0.5) * vec2(1.0, 0.85));
|
||||
|
||||
return vec4(pi + ca - cb * ma, e, f);
|
||||
}
|
||||
|
||||
|
||||
float hash1(vec2 p) {
|
||||
float n = dot(p, vec2(127.1, 311.7));
|
||||
return fract(sin(n) * 43758.5453);
|
||||
}
|
||||
|
||||
vec4 getProceduralColor() {
|
||||
vec2 uv = _position.xz + 0.5;
|
||||
vec2 pos = _position.xz * 40.0;
|
||||
// gray
|
||||
vec4 h = hexagon(8.0 * pos + 0.5);
|
||||
float n = snoise(vec3(0.3 * h.xy + iGlobalTime * 0.1, iGlobalTime));
|
||||
vec3 col = 0.15 + 0.15 * hash1(h.xy + 1.2) * vec3(1.0);
|
||||
col *= smoothstep(0.10, 0.11, h.z);
|
||||
col *= smoothstep(0.10, 0.11, h.w);
|
||||
col *= 1.0 + 0.15 * sin(40.0 * h.z);
|
||||
col *= 0.75 + 0.5 * h.z * n;
|
||||
col *= pow(16.0 * uv.x * (1.0 - uv.x) * uv.y * (1.0 - uv.y), 0.1);
|
||||
return vec4(col, 1.0);
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
#include <gpu/Batch.h>
|
||||
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <ObjectMotionState.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
|
@ -25,15 +26,31 @@ EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID,
|
|||
return std::make_shared<RenderableBoxEntityItem>(entityID, properties);
|
||||
}
|
||||
|
||||
void RenderableBoxEntityItem::setUserData(const QString& value) {
|
||||
if (value != getUserData()) {
|
||||
BoxEntityItem::setUserData(value);
|
||||
_procedural.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableBoxEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
|
||||
Q_ASSERT(getType() == EntityTypes::Box);
|
||||
glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
|
||||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
|
||||
|
||||
if (!_procedural) {
|
||||
_procedural.reset(new ProceduralInfo(this));
|
||||
}
|
||||
|
||||
if (_procedural->ready()) {
|
||||
_procedural->prepare(batch);
|
||||
DependencyManager::get<GeometryCache>()->renderUnitCube(batch);
|
||||
} else {
|
||||
glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
|
||||
}
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
};
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
|
||||
#include <BoxEntityItem.h>
|
||||
#include "RenderableEntityItem.h"
|
||||
#include "RenderableProceduralItem.h"
|
||||
|
||||
class RenderableBoxEntityItem : public BoxEntityItem {
|
||||
class RenderableBoxEntityItem : public BoxEntityItem, RenderableProceduralItem {
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
|
@ -24,6 +25,7 @@ public:
|
|||
{ }
|
||||
|
||||
virtual void render(RenderArgs* args);
|
||||
virtual void setUserData(const QString& value);
|
||||
|
||||
SIMPLE_RENDERABLE()
|
||||
};
|
||||
|
|
137
libraries/entities-renderer/src/RenderableProceduralItem.cpp
Normal file
137
libraries/entities-renderer/src/RenderableProceduralItem.cpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/09/05
|
||||
// Copyright 2013-2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "RenderableProceduralItem.h"
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
||||
#include <ShaderCache.h>
|
||||
#include <EntityItem.h>
|
||||
#include <TextureCache.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <gpu/Batch.h>
|
||||
|
||||
#include "RenderableProceduralItemShader.h"
|
||||
#include "../render-utils/simple_vert.h"
|
||||
|
||||
static const char* const UNIFORM_TIME_NAME= "iGlobalTime";
|
||||
static const char* const UNIFORM_SCALE_NAME = "iWorldScale";
|
||||
static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity";
|
||||
|
||||
RenderableProceduralItem::ProceduralInfo::ProceduralInfo(EntityItem* entity) : _entity(entity) {
|
||||
QJsonObject userData;
|
||||
{
|
||||
const QString& userDataJson = entity->getUserData();
|
||||
if (userDataJson.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QJsonParseError parseError;
|
||||
auto doc = QJsonDocument::fromJson(userDataJson.toUtf8(), &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
return;
|
||||
}
|
||||
userData = doc.object();
|
||||
}
|
||||
|
||||
// Example
|
||||
//{
|
||||
// "ProceduralEntity": {
|
||||
// "shaderUrl": "file:///C:/Users/bdavis/Git/hifi/examples/shaders/test.fs",
|
||||
// "color" : "#FFFFFF"
|
||||
// }
|
||||
//}
|
||||
|
||||
auto proceduralData = userData[PROCEDURAL_USER_DATA_KEY];
|
||||
if (proceduralData.isNull()) {
|
||||
return;
|
||||
}
|
||||
auto proceduralDataObject = proceduralData.toObject();
|
||||
QString shaderUrl = proceduralDataObject["shaderUrl"].toString();
|
||||
_shaderUrl = QUrl(shaderUrl);
|
||||
if (!_shaderUrl.isValid()) {
|
||||
qWarning() << "Invalid shader URL: " << shaderUrl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_shaderUrl.isLocalFile()) {
|
||||
_shaderPath = _shaderUrl.toLocalFile();
|
||||
qDebug() << "Shader path: " << _shaderPath;
|
||||
if (!QFile(_shaderPath).exists()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Shader url: " << _shaderUrl;
|
||||
_networkShader = ShaderCache::instance().getShader(_shaderUrl);
|
||||
}
|
||||
|
||||
_enabled = true;
|
||||
}
|
||||
|
||||
bool RenderableProceduralItem::ProceduralInfo::ready() {
|
||||
if (!_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_shaderPath.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_networkShader) {
|
||||
return _networkShader->isLoaded();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderableProceduralItem::ProceduralInfo::prepare(gpu::Batch& batch) {
|
||||
if (_shaderUrl.isLocalFile()) {
|
||||
auto lastModified = QFileInfo(_shaderPath).lastModified().toMSecsSinceEpoch();
|
||||
if (lastModified > _shaderModified) {
|
||||
QFile file(_shaderPath);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
_shaderSource = QTextStream(&file).readAll();
|
||||
_pipelineDirty = true;
|
||||
_shaderModified = lastModified;
|
||||
}
|
||||
} else if (_networkShader && _networkShader->isLoaded()) {
|
||||
_shaderSource = _networkShader->_source;
|
||||
}
|
||||
|
||||
if (!_pipeline || _pipelineDirty) {
|
||||
_pipelineDirty = false;
|
||||
if (!_vertexShader) {
|
||||
_vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(simple_vert)));
|
||||
}
|
||||
QString framentShaderSource = SHADER_TEMPLATE.arg(_shaderSource);
|
||||
_fragmentShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(framentShaderSource.toLocal8Bit().data())));
|
||||
_shader = gpu::ShaderPointer(gpu::Shader::createProgram(_vertexShader, _fragmentShader));
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT));
|
||||
gpu::Shader::makeProgram(*_shader, slotBindings);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setCullMode(gpu::State::CULL_NONE);
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(false,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
_pipeline = gpu::PipelinePointer(gpu::Pipeline::create(_shader, state));
|
||||
_timeSlot = _shader->getUniforms().findLocation(UNIFORM_TIME_NAME);
|
||||
_scaleSlot = _shader->getUniforms().findLocation(UNIFORM_SCALE_NAME);
|
||||
_start = usecTimestampNow();
|
||||
}
|
||||
|
||||
batch.setPipeline(_pipeline);
|
||||
float time = (float)((usecTimestampNow() - _start) / USECS_PER_MSEC) / MSECS_PER_SECOND;
|
||||
batch._glUniform1f(_timeSlot, time);
|
||||
auto scale = _entity->getDimensions();
|
||||
batch._glUniform3f(_scaleSlot, scale.x, scale.y, scale.z);
|
||||
batch.setResourceTexture(DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT, DependencyManager::get<TextureCache>()->getNormalFittingTexture());
|
||||
}
|
51
libraries/entities-renderer/src/RenderableProceduralItem.h
Normal file
51
libraries/entities-renderer/src/RenderableProceduralItem.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/09/05
|
||||
// Copyright 2013-2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_RenderableProcedrualItem_h
|
||||
#define hifi_RenderableProcedrualItem_h
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#include <ShaderCache.h>
|
||||
#include <gpu/Shader.h>
|
||||
#include <gpu/Pipeline.h>
|
||||
#include <gpu/Batch.h>
|
||||
|
||||
class EntityItem;
|
||||
|
||||
class RenderableProceduralItem {
|
||||
protected:
|
||||
struct ProceduralInfo {
|
||||
ProceduralInfo(EntityItem* entity);
|
||||
bool ready();
|
||||
void prepare(gpu::Batch& batch);
|
||||
|
||||
bool _enabled{ false };
|
||||
gpu::PipelinePointer _pipeline;
|
||||
gpu::ShaderPointer _vertexShader;
|
||||
gpu::ShaderPointer _fragmentShader;
|
||||
gpu::ShaderPointer _shader;
|
||||
QString _shaderSource;
|
||||
QString _shaderPath;
|
||||
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;
|
||||
EntityItem* _entity;
|
||||
};
|
||||
|
||||
QSharedPointer<ProceduralInfo> _procedural;
|
||||
};
|
||||
|
||||
#endif
|
329
libraries/entities-renderer/src/RenderableProceduralItemShader.h
Normal file
329
libraries/entities-renderer/src/RenderableProceduralItemShader.h
Normal file
|
@ -0,0 +1,329 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/09/05
|
||||
// Copyright 2013-2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// Shader includes portions of webgl-noise:
|
||||
// Description : Array and textureless GLSL 2D/3D/4D simplex
|
||||
// noise functions.
|
||||
// Author : Ian McEwan, Ashima Arts.
|
||||
// Maintainer : ijm
|
||||
// Lastmod : 20110822 (ijm)
|
||||
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
|
||||
// Distributed under the MIT License. See LICENSE file.
|
||||
// https://github.com/ashima/webgl-noise
|
||||
//
|
||||
const QString SHADER_TEMPLATE = R"SCRIBE(#version 410 core
|
||||
layout(location = 0) out vec4 _fragColor0;
|
||||
layout(location = 1) out vec4 _fragColor1;
|
||||
layout(location = 2) out vec4 _fragColor2;
|
||||
|
||||
// the glow intensity
|
||||
uniform float glowIntensity;
|
||||
|
||||
// the alpha threshold
|
||||
uniform float alphaThreshold;
|
||||
|
||||
uniform sampler2D normalFittingMap;
|
||||
|
||||
vec3 bestFitNormal(vec3 normal) {
|
||||
vec3 absNorm = abs(normal);
|
||||
float maxNAbs = max(absNorm.z, max(absNorm.x, absNorm.y));
|
||||
|
||||
vec2 texcoord = (absNorm.z < maxNAbs ?
|
||||
(absNorm.y < maxNAbs ? absNorm.yz : absNorm.xz) :
|
||||
absNorm.xy);
|
||||
texcoord = (texcoord.x < texcoord.y ? texcoord.yx : texcoord.xy);
|
||||
texcoord.y /= texcoord.x;
|
||||
vec3 cN = normal / maxNAbs;
|
||||
float fittingScale = texture(normalFittingMap, texcoord).a;
|
||||
cN *= fittingScale;
|
||||
return (cN * 0.5 + 0.5);
|
||||
}
|
||||
|
||||
|
||||
const vec3 DEFAULT_SPECULAR = vec3(0.1);
|
||||
const float DEFAULT_SHININESS = 10;
|
||||
|
||||
void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess, vec3 emissive) {
|
||||
if (alpha != glowIntensity) {
|
||||
discard;
|
||||
}
|
||||
|
||||
_fragColor0 = vec4(diffuse.rgb, alpha);
|
||||
_fragColor1 = vec4(bestFitNormal(normal), 0.5);
|
||||
_fragColor2 = vec4(emissive, shininess / 128.0);
|
||||
}
|
||||
|
||||
float mod289(float x) {
|
||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||
}
|
||||
|
||||
vec2 mod289(vec2 x) {
|
||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||
}
|
||||
|
||||
vec3 mod289(vec3 x) {
|
||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||
}
|
||||
|
||||
vec4 mod289(vec4 x) {
|
||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||
}
|
||||
|
||||
float permute(float x) {
|
||||
return mod289(((x*34.0)+1.0)*x);
|
||||
}
|
||||
|
||||
vec3 permute(vec3 x) {
|
||||
return mod289(((x*34.0)+1.0)*x);
|
||||
}
|
||||
|
||||
vec4 permute(vec4 x) {
|
||||
return mod289(((x*34.0)+1.0)*x);
|
||||
}
|
||||
|
||||
float taylorInvSqrt(float r) {
|
||||
return 1.79284291400159 - 0.85373472095314 * r;
|
||||
}
|
||||
|
||||
vec4 taylorInvSqrt(vec4 r) {
|
||||
return 1.79284291400159 - 0.85373472095314 * r;
|
||||
}
|
||||
|
||||
vec4 grad4(float j, vec4 ip) {
|
||||
const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
|
||||
vec4 p, s;
|
||||
|
||||
p.xyz = floor(fract(vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
|
||||
p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
|
||||
s = vec4(lessThan(p, vec4(0.0)));
|
||||
p.xyz = p.xyz + (s.xyz * 2.0 - 1.0) * s.www;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// (sqrt(5) - 1)/4 = F4, used once below
|
||||
#define F4 0.309016994374947451
|
||||
|
||||
float snoise(vec4 v) {
|
||||
const vec4 C = vec4(0.138196601125011, // (5 - sqrt(5))/20 G4
|
||||
0.276393202250021, // 2 * G4
|
||||
0.414589803375032, // 3 * G4
|
||||
-0.447213595499958); // -1 + 4 * G4
|
||||
|
||||
// First corner
|
||||
vec4 i = floor(v + dot(v, vec4(F4)));
|
||||
vec4 x0 = v - i + dot(i, C.xxxx);
|
||||
|
||||
// Other corners
|
||||
|
||||
// Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
|
||||
vec4 i0;
|
||||
vec3 isX = step(x0.yzw, x0.xxx);
|
||||
vec3 isYZ = step(x0.zww, x0.yyz);
|
||||
i0.x = isX.x + isX.y + isX.z;
|
||||
i0.yzw = 1.0 - isX;
|
||||
i0.y += isYZ.x + isYZ.y;
|
||||
i0.zw += 1.0 - isYZ.xy;
|
||||
i0.z += isYZ.z;
|
||||
i0.w += 1.0 - isYZ.z;
|
||||
|
||||
// i0 now contains the unique values 0,1,2,3 in each channel
|
||||
vec4 i3 = clamp(i0, 0.0, 1.0);
|
||||
vec4 i2 = clamp(i0 - 1.0, 0.0, 1.0);
|
||||
vec4 i1 = clamp(i0 - 2.0, 0.0, 1.0);
|
||||
|
||||
vec4 x1 = x0 - i1 + C.xxxx;
|
||||
vec4 x2 = x0 - i2 + C.yyyy;
|
||||
vec4 x3 = x0 - i3 + C.zzzz;
|
||||
vec4 x4 = x0 + C.wwww;
|
||||
|
||||
// Permutations
|
||||
i = mod289(i);
|
||||
float j0 = permute(permute(permute(permute(i.w) + i.z) + i.y) + i.x);
|
||||
vec4 j1 = permute(
|
||||
permute(
|
||||
permute(
|
||||
permute(i.w + vec4(i1.w, i2.w, i3.w, 1.0)) + i.z
|
||||
+ vec4(i1.z, i2.z, i3.z, 1.0)) + i.y
|
||||
+ vec4(i1.y, i2.y, i3.y, 1.0)) + i.x
|
||||
+ vec4(i1.x, i2.x, i3.x, 1.0));
|
||||
|
||||
// Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope
|
||||
// 7*7*6 = 294, which is close to the ring size 17*17 = 289.
|
||||
vec4 ip = vec4(1.0 / 294.0, 1.0 / 49.0, 1.0 / 7.0, 0.0);
|
||||
|
||||
vec4 p0 = grad4(j0, ip);
|
||||
vec4 p1 = grad4(j1.x, ip);
|
||||
vec4 p2 = grad4(j1.y, ip);
|
||||
vec4 p3 = grad4(j1.z, ip);
|
||||
vec4 p4 = grad4(j1.w, ip);
|
||||
|
||||
// Normalise gradients
|
||||
vec4 norm = taylorInvSqrt(
|
||||
vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
|
||||
p0 *= norm.x;
|
||||
p1 *= norm.y;
|
||||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
p4 *= taylorInvSqrt(dot(p4, p4));
|
||||
|
||||
// Mix contributions from the five corners
|
||||
vec3 m0 = max(0.6 - vec3(dot(x0, x0), dot(x1, x1), dot(x2, x2)), 0.0);
|
||||
vec2 m1 = max(0.6 - vec2(dot(x3, x3), dot(x4, x4)), 0.0);
|
||||
m0 = m0 * m0;
|
||||
m1 = m1 * m1;
|
||||
return 49.0
|
||||
* (dot(m0 * m0, vec3(dot(p0, x0), dot(p1, x1), dot(p2, x2)))
|
||||
+ dot(m1 * m1, vec2(dot(p3, x3), dot(p4, x4))));
|
||||
|
||||
}
|
||||
|
||||
float snoise(vec3 v) {
|
||||
const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
|
||||
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
|
||||
|
||||
// First corner
|
||||
vec3 i = floor(v + dot(v, C.yyy));
|
||||
vec3 x0 = v - i + dot(i, C.xxx);
|
||||
|
||||
// Other corners
|
||||
vec3 g = step(x0.yzx, x0.xyz);
|
||||
vec3 l = 1.0 - g;
|
||||
vec3 i1 = min(g.xyz, l.zxy);
|
||||
vec3 i2 = max(g.xyz, l.zxy);
|
||||
|
||||
vec3 x1 = x0 - i1 + C.xxx;
|
||||
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
|
||||
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
|
||||
|
||||
// Permutations
|
||||
i = mod289(i);
|
||||
vec4 p = permute(
|
||||
permute(
|
||||
permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y
|
||||
+ vec4(0.0, i1.y, i2.y, 1.0)) + i.x
|
||||
+ vec4(0.0, i1.x, i2.x, 1.0));
|
||||
|
||||
// Gradients: 7x7 points over a square, mapped onto an octahedron.
|
||||
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
|
||||
float n_ = 0.142857142857; // 1.0/7.0
|
||||
vec3 ns = n_ * D.wyz - D.xzx;
|
||||
|
||||
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
|
||||
|
||||
vec4 x_ = floor(j * ns.z);
|
||||
vec4 y_ = floor(j - 7.0 * x_); // mod(j,N)
|
||||
|
||||
vec4 x = x_ * ns.x + ns.yyyy;
|
||||
vec4 y = y_ * ns.x + ns.yyyy;
|
||||
vec4 h = 1.0 - abs(x) - abs(y);
|
||||
|
||||
vec4 b0 = vec4(x.xy, y.xy);
|
||||
vec4 b1 = vec4(x.zw, y.zw);
|
||||
|
||||
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
|
||||
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
|
||||
vec4 s0 = floor(b0) * 2.0 + 1.0;
|
||||
vec4 s1 = floor(b1) * 2.0 + 1.0;
|
||||
vec4 sh = -step(h, vec4(0.0));
|
||||
|
||||
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
|
||||
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
|
||||
|
||||
vec3 p0 = vec3(a0.xy, h.x);
|
||||
vec3 p1 = vec3(a0.zw, h.y);
|
||||
vec3 p2 = vec3(a1.xy, h.z);
|
||||
vec3 p3 = vec3(a1.zw, h.w);
|
||||
|
||||
//Normalise gradients
|
||||
vec4 norm = taylorInvSqrt(
|
||||
vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
|
||||
p0 *= norm.x;
|
||||
p1 *= norm.y;
|
||||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
|
||||
// Mix final noise value
|
||||
vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)),
|
||||
0.0);
|
||||
m = m * m;
|
||||
return 42.0
|
||||
* dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
|
||||
}
|
||||
|
||||
float snoise(vec2 v) {
|
||||
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
|
||||
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
|
||||
-0.577350269189626, // -1.0 + 2.0 * C.x
|
||||
0.024390243902439); // 1.0 / 41.0
|
||||
// First corner
|
||||
vec2 i = floor(v + dot(v, C.yy));
|
||||
vec2 x0 = v - i + dot(i, C.xx);
|
||||
|
||||
// Other corners
|
||||
vec2 i1;
|
||||
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
||||
vec4 x12 = x0.xyxy + C.xxzz;
|
||||
x12.xy -= i1;
|
||||
|
||||
// Permutations
|
||||
i = mod289(i); // Avoid truncation effects in permutation
|
||||
vec3 p = permute(
|
||||
permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
|
||||
|
||||
vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)),
|
||||
0.0);
|
||||
m = m * m;
|
||||
m = m * m;
|
||||
|
||||
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
|
||||
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
|
||||
|
||||
vec3 x = 2.0 * fract(p * C.www) - 1.0;
|
||||
vec3 h = abs(x) - 0.5;
|
||||
vec3 ox = floor(x + 0.5);
|
||||
vec3 a0 = x - ox;
|
||||
|
||||
// Normalise gradients implicitly by scaling m
|
||||
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
|
||||
m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
|
||||
|
||||
// Compute final noise value at P
|
||||
vec3 g;
|
||||
g.x = a0.x * x0.x + h.x * x0.y;
|
||||
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
|
||||
return 130.0 * dot(m, g);
|
||||
}
|
||||
|
||||
// the interpolated normal
|
||||
in vec3 _normal;
|
||||
in vec3 _color;
|
||||
in vec2 _texCoord0;
|
||||
in vec4 _position;
|
||||
|
||||
// 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
|
||||
|
||||
%1
|
||||
|
||||
void main(void) {
|
||||
vec4 texel = getProceduralColor();
|
||||
|
||||
packDeferredFragmentLightmap(
|
||||
normalize(_normal),
|
||||
glowIntensity * texel.a,
|
||||
_color.rgb,
|
||||
DEFAULT_SPECULAR, DEFAULT_SHININESS,
|
||||
texel.rgb);
|
||||
}
|
||||
)SCRIBE";
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <DependencyManager.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "RenderableDebugableEntityItem.h"
|
||||
|
@ -25,21 +26,38 @@ EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entity
|
|||
return std::make_shared<RenderableSphereEntityItem>(entityID, properties);
|
||||
}
|
||||
|
||||
void RenderableSphereEntityItem::setUserData(const QString& value) {
|
||||
if (value != getUserData()) {
|
||||
SphereEntityItem::setUserData(value);
|
||||
_procedural.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableSphereEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableSphereEntityItem::render");
|
||||
Q_ASSERT(getType() == EntityTypes::Sphere);
|
||||
glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha());
|
||||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setModelTransform(getTransformToCenter()); // use a transform with scale, rotation, registration point and translation
|
||||
|
||||
// TODO: it would be cool to select different slices/stacks geometry based on the size of the sphere
|
||||
// and the distance to the viewer. This would allow us to reduce the triangle count for smaller spheres
|
||||
// that aren't close enough to see the tessellation and use larger triangle count for spheres that would
|
||||
// expose that effect
|
||||
const int SLICES = 15, STACKS = 15;
|
||||
static const int SLICES = 15, STACKS = 15;
|
||||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setModelTransform(getTransformToCenter()); // use a transform with scale, rotation, registration point and translation
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
|
||||
if (!_procedural) {
|
||||
_procedural.reset(new ProceduralInfo(this));
|
||||
}
|
||||
|
||||
if (_procedural->ready()) {
|
||||
_procedural->prepare(batch);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(batch, 0.5f, SLICES, STACKS, vec3(1));
|
||||
} else {
|
||||
glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha());
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
|
||||
}
|
||||
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
};
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
#define hifi_RenderableSphereEntityItem_h
|
||||
|
||||
#include <SphereEntityItem.h>
|
||||
|
||||
#include "RenderableEntityItem.h"
|
||||
#include "RenderableProceduralItem.h"
|
||||
|
||||
class RenderableSphereEntityItem : public SphereEntityItem {
|
||||
class RenderableSphereEntityItem : public SphereEntityItem, RenderableProceduralItem {
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
|
@ -25,6 +25,7 @@ public:
|
|||
{ }
|
||||
|
||||
virtual void render(RenderArgs* args);
|
||||
virtual void setUserData(const QString& value);
|
||||
|
||||
SIMPLE_RENDERABLE();
|
||||
};
|
||||
|
|
|
@ -334,7 +334,7 @@ public:
|
|||
void setLocked(bool value) { _locked = value; }
|
||||
|
||||
const QString& getUserData() const { return _userData; }
|
||||
void setUserData(const QString& value) { _userData = value; }
|
||||
virtual void setUserData(const QString& value) { _userData = value; }
|
||||
|
||||
const SimulationOwner& getSimulationOwner() const { return _simulationOwner; }
|
||||
void setSimulationOwner(const QUuid& id, quint8 priority);
|
||||
|
|
32
libraries/render-utils/src/ShaderCache.cpp
Normal file
32
libraries/render-utils/src/ShaderCache.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/05/26
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "ShaderCache.h"
|
||||
|
||||
NetworkShader::NetworkShader(const QUrl& url, bool delayLoad)
|
||||
: Resource(url, delayLoad) {};
|
||||
|
||||
void NetworkShader::downloadFinished(QNetworkReply* reply) {
|
||||
if (reply) {
|
||||
_source = reply->readAll();
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
ShaderCache& ShaderCache::instance() {
|
||||
static ShaderCache _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
NetworkShaderPointer ShaderCache::getShader(const QUrl& url) {
|
||||
return ResourceCache::getResource(url, QUrl(), false, nullptr).staticCast<NetworkShader>();
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||
return QSharedPointer<Resource>(new NetworkShader(url, delayLoad), &Resource::allReferencesCleared);
|
||||
}
|
||||
|
34
libraries/render-utils/src/ShaderCache.h
Normal file
34
libraries/render-utils/src/ShaderCache.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/05/26
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_ShaderCache_h
|
||||
#define hifi_ShaderCache_h
|
||||
|
||||
#include <ResourceCache.h>
|
||||
|
||||
class NetworkShader : public Resource {
|
||||
public:
|
||||
NetworkShader(const QUrl& url, bool delayLoad);
|
||||
virtual void downloadFinished(QNetworkReply* reply) override;
|
||||
|
||||
QByteArray _source;
|
||||
};
|
||||
|
||||
using NetworkShaderPointer = QSharedPointer<NetworkShader>;
|
||||
|
||||
class ShaderCache : public ResourceCache {
|
||||
public:
|
||||
static ShaderCache& instance();
|
||||
|
||||
NetworkShaderPointer getShader(const QUrl& url);
|
||||
|
||||
protected:
|
||||
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) override;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue