介绍
几年前工余时间我做了一个开源的渲染器,目的是为了一些MultiPass的DEMO用, 最近把他扩展为,兼容GLSL的SHADER,也兼容SHADERTOY的大多数的DEMO。我放这篇文是希望有兴趣的人可以用这个DEMO及一起共同开发。
有興趣可以一起共同开发連系本人WX: MacLinWechat
-
WebcamTexture
-
MicrophoneTexture
-
SoundCloudTexture
-
FrameBufferTextureCubeMap
它也兼容了Multi-pass Rendering和以下的Texture类型。
- KeyboardTexture
- VideoTexture
- FrameBufferTexture2D
将来也支持以下的Texture类型
- WebcamTexture
- MicrophoneTexture
- SoundCloudTexture
- FrameBufferTextureCubeMap
代码结构
整个项目的结构,我们有一个Demo类
- Pass类
- RenderStates类
- ShaderProgram类
- Channel类
- FrameBuffer类
它们的关系如下:
Pass类
一个Pass指的是一遍渲染过程,他将Channels的Shader,各类的Texture,渲染到FrameBuffer或RenderTarget上。
RenderState类
RenderState主要是控制viewport的大少尺寸,其他的RenderState没有影响。
ShaderProgram类
是负责渲染算法:比如
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
fragColor = vec4(0.0,0.0,1.0,1.0);
}
Channels类
Channels是一个pass的输入参数,输入参数包括:
- Texture
- FrameBuffer
BufferA/BufferB/BufferC/BufferD/Sound/CubeMap
- Filter
Nearest/Linear/MipMap
- wrap
Clamp/Repeat
Texture分类
我们有以下texture分类
- KeyBoardTexture
- MicroPhoneTexture
- VideoTexture
- WebcamTexture
- TextureCubeMap
- FrameBufferTexture2D/FrameBufferTextureCubeMap
KeyBoardTexture
一个256*1大小的Texture,当某一个ascii按键按下,比如‘a’按下。
texture(keyboard, ‘a’)便会回传1.0。如此类推,x=0是ascii的0,x=255是ascii的255。
MicroPhoneTexture
一个256*1大小的Texture,是过去256个sample的数值。
VideoTexture
视频文件的texture
WebcamTexture
相机输入的texture
Texture2D
图片文件的texture
TextureCubeMap
图片文件的cubemap
FrameBufferTexture2D/FrameBufferTextureCubeMap
上一帧此framebuffer的渲染结果的一个texture
https://github.com/MacgyverLin/GLSLRayMarching.gitgithub.com
配置
因為每個DEMO都是一組PASS組成,而PASS又由Texture,FrameBuffer,Filter, wrap
所以我們可以用一個JSON來配置一下, 比如說:
"demo":{
"passes": [
{
"shader": "",
"ichannel0": {
...
},
"ichannel1": {
...
},
"rendertarget": ""
},
{
"shader": "",
"ichannel0": {
...
},
"ichannel1": {
...
},
"rendertarget": ""
},
{
"shader": "",
"ichannel0": {
...
},
"ichannel1": {
...
},
"rendertarget": "image"
}
]
}
實際一點的例子
{
"passes": [
{
"shader": "buffera.glsl",
"ichannel0": {
"buffer": "buffera",
"filter": "linear",
"wrap": "clamp"
},
"ichannel1": {
"buffer": "bufferb",
"filter": "linear",
"wrap": "clamp"
},
"rendertarget": "buffera"
},
{
"shader": "bufferb.glsl",
"ichannel0": {
"buffer": "bufferb",
"filter": "linear",
"wrap": "clamp"
},
"ichannel1": {
"buffer": "buffera",
"filter": "linear",
"wrap": "clamp"
},
"rendertarget": "bufferb"
},
{
"shader": "image.glsl",
"ichannel0": {
"buffer": "buffera",
"filter": "linear",
"wrap": "clamp"
},
"ichannel1": {
"texture2d": "tex0.png",
"filter": "mipmap",
"wrap": "repeat",
"vflip": true
},
"rendertarget": "image"
}
]
}
展示
测试一下来自SHADERTOY的DEMO
以上來之shadertoy的,通過我的渲染器輸出的。
源碼
因為還有一些Texture類沒有實現,如大家有興趣可以一起共同開發:
比如
- WebcamTexture
- MicrophoneTexture
- SoundCloudTexture
- FrameBufferTextureCubeMap
https://github.com/MacgyverLin/GLSLRayMarching.gitgithub.com
class KeyboardTexture : public Texture2D
{
public:
KeyboardTexture()
: Texture2D()
, buffer(256 * 3)
{
}
virtual ~KeyboardTexture()
{
}
bool Initiate()
{
if (!Texture2D::Initiate(256, 3, 1, Texture::DynamicRange::LOW, &buffer[0]))
return false;
SetMinFilter(Texture::MinFilter::Nearest);
SetMagFilter(Texture::MagFilter::Nearest);
SetWarpS(Texture::Wrap::Clamp);
SetWarpR(Texture::Wrap::Clamp);
SetWarpT(Texture::Wrap::Clamp);
return true;
}
void UpdateKey(int key)
{
char* keydown = &buffer[256 * 0];
char* keyclick = &buffer[256 * 1];
char* keytoggle = &buffer[256 * 2];
int oldKeydown = keydown[key];
int keyState = GetAsyncKeyState(key);
keydown[key] = (keyState & 0x8000) != 0;
keyclick[key] = (keydown[key] != oldKeydown) && keydown[key];
keytoggle[key] = (keyState & 0x0001) != 0;
/*
bool release = (keydown[key] != oldKeydown) && !keydown[key];
if (key == 38)
{
//Debug("%d %d %d\n", keydown[key], keyclick[key], keytoggle[key]);
if (keyclick[key])
{
int a = 1;
}
}
*/
}
virtual void Tick()
{
for (int i = 'a'; i < 'z'; i++)
{
UpdateKey(i);
}
for (int i = 'A'; i < 'Z'; i++)
{
UpdateKey(i);
}
UpdateKey(VK_LEFT);
UpdateKey(VK_UP);
UpdateKey(VK_RIGHT);
UpdateKey(VK_DOWN);
UpdateKey(VK_PRIOR);
UpdateKey(VK_NEXT);
UpdateKey(VK_END);
UpdateKey(VK_HOME);
UpdateKey(VK_SHIFT);
UpdateKey(VK_CONTROL);
UpdateKey(VK_MENU);
Texture2D::Update(&buffer[0]);
}
private:
private:
std::vector<char> buffer;
};
class UniformGUI
{
public:
UniformGUI()
{
}
virtual ~UniformGUI()
{
}
bool Initiate()
{
return true;
}
virtual void Update(ShaderProgram& shaderProgram)
{
GUI::UpdateShader(shaderProgram);
}
private:
private:
};
class WebcamTexture : public Texture2D
{
public:
WebcamTexture()
: Texture2D()
, buffer(1280 * 720 * 4)
{
}
virtual ~WebcamTexture()
{
}
bool Initiate(bool vflip_)
{
return Texture2D::Initiate(1280, 720, 4, Texture::DynamicRange::LOW, &buffer[0]);
}
virtual void Update()
{
Texture2D::Update(&buffer[0]);
}
private:
private:
std::vector<char> buffer;
};
class MicrophoneTexture : public Texture2D
{
public:
MicrophoneTexture()
: Texture2D()
, buffer(1280 * 720 * 4)
{
}
virtual ~MicrophoneTexture()
{
}
bool Initiate()
{
return Texture2D::Initiate(512, 2, 1, Texture::DynamicRange::LOW, &buffer[0]);
}
virtual void Update()
{
Texture2D::Update(&buffer[0]);
}
private:
private:
std::vector<char> buffer;
};
class SoundCloudTexture : public Texture2D
{
public:
SoundCloudTexture()
: Texture2D()
, buffer(1280 * 720 * 4)
{
}
virtual ~SoundCloudTexture()
{
}
bool Initiate(const std::string& url, bool vflip_)
{
return Texture2D::Initiate(512, 2, 1, Texture::DynamicRange::LOW, &buffer[0]);
}
virtual void Update()
{
Texture2D::Update(&buffer[0]);
}
private:
private:
std::vector<char> buffer;
};
class VideoTexture : public Texture2D
{
public:
VideoTexture()
: Texture2D()
, buffer(1280 * 720 * 4)
{
}
virtual ~VideoTexture()
{
}
bool Initiate(const std::string& url, bool vflip_)
{
return Texture2D::Initiate(1280, 720, 4, Texture::DynamicRange::LOW, &buffer[0]);
}
virtual void Update()
{
Texture2D::Update(&buffer[0]);
}
private:
private:
std::vector<char> buffer;
};
class FlipFrameBuffer : public FrameBuffer
{
public:
FlipFrameBuffer()
: FrameBuffer()
, current(0)
{
}
virtual ~FlipFrameBuffer()
{
}
virtual void Flip()
{
current = 1 - current;
}
virtual Texture* GetCurrentTexture() = 0;
protected:
int GetCurrent()
{
return current;
}
private:
int current;
};
class FlipTexture2DFrameBuffer : public FlipFrameBuffer
{
public:
FlipTexture2DFrameBuffer()
: FlipFrameBuffer()
{
}
virtual ~FlipTexture2DFrameBuffer()
{
}
virtual bool Initiate(unsigned int width, unsigned int height, unsigned int nrComponents, Texture::DynamicRange dynamicRange_)
{
if (!FlipFrameBuffer::Initiate())
return false;
if (!texture[0].Initiate(width, height, nrComponents, dynamicRange_, nullptr))
return false;
if (!texture[1].Initiate(width, height, nrComponents, dynamicRange_, nullptr))
return false;
SetColorAttachment(FrameBuffer::ColorAttachment::COLOR_ATTACHMENT0, &texture[0]);
return true;
}
virtual void Terminate()
{
for (int i = 0; i < 2; i++)
texture[i].Terminate();
return FlipFrameBuffer::Terminate();
}
Texture* GetCurrentTexture()
{
return &texture[1 - GetCurrent()];
}
virtual void Flip()
{
FlipFrameBuffer::Flip();
SetColorAttachment(FrameBuffer::ColorAttachment::COLOR_ATTACHMENT0, &texture[GetCurrent()]);
}
private:
Texture2D texture[2];
};
class FlipTextureCubeMapFrameBuffer : public FlipFrameBuffer
{
public:
FlipTextureCubeMapFrameBuffer()
: FlipFrameBuffer()
{
}
virtual ~FlipTextureCubeMapFrameBuffer()
{
}
virtual bool Initiate(unsigned int size, unsigned int nrComponents, Texture::DynamicRange dynamicRange_)
{
if (!FlipFrameBuffer::Initiate())
return false;
if (!texture[0].Initiate(size, nrComponents, dynamicRange_, nullptr))
return false;
if (!texture[1].Initiate(size, nrComponents, dynamicRange_, nullptr))
return false;
SetColorAttachment(FrameBuffer::ColorAttachment::COLOR_ATTACHMENT0, &texture[0]);
return true;
}
virtual void Terminate()
{
for (int i = 0; i < 2; i++)
texture[i].Terminate();
return FlipFrameBuffer::Terminate();
}
Texture* GetCurrentTexture()
{
return &texture[1 - GetCurrent()];
}
virtual void Flip()
{
FlipFrameBuffer::Flip();
//Invalidate();
SetColorAttachment(FrameBuffer::ColorAttachment::COLOR_ATTACHMENT0, &texture[GetCurrent()]);
}
private:
TextureCubeMap texture[2];
};
#define SOUND 0
#define BUFFER_A 1
#define BUFFER_B 2
#define BUFFER_C 3
#define BUFFER_D 4
#define CUBEMAP_A 5
#define IMAGE 6
#define CHANNEL_COUNT 4
template<class ValueType>
class Parameter
{
public:
Parameter()
{
}
~Parameter()
{
}
void SetValue(const ValueType& value_)
{
value = value_;
}
const ValueType& GetValue() const
{
return value;
}
protected:
std::string name;
ValueType value;
};
template<class ValueType, class RangeType>
class ParameterWithRange : public Parameter<ValueType>
{
public:
ParameterWithRange()
{
}
~ParameterWithRange()
{
}
void SetMin(const RangeType& min_)
{
min = min_;
}
void SetMax(const RangeType& max_)
{
max = max_;
}
const RangeType& GetMin() const
{
return min;
}
const RangeType& GetMax() const
{
return max;
}
protected:
RangeType min;
RangeType max;
};
class Parameters
{
public:
Parameters()
{
}
~Parameters()
{
}
bool CreateBool(const Value& boolParamJson)
{
return true;
}
bool CreateBVector2(const Value& bVector2ParamJson)
{
return true;
}
bool CreateBVector3(const Value& bVector3ParamJson)
{
return true;
}
bool CreateBVector4(const Value& bVector4ParamJson)
{
return true;
}
bool CreateInt(const Value& intParamJson)
{
return true;
}
bool CreateIVector2(const Value& int2ParamJson)
{
return true;
}
bool CreateIVector3(const Value& int3ParamJson)
{
return true;
}
bool CreateIVector4(const Value& int4ParamJson)
{
return true;
}
bool CreateUInt(const Value& uintParamJson)
{
return true;
}
bool CreateUVector2(const Value& uint2ParamJson)
{
return true;
}
bool CreateUVector3(const Value& uint3ParamJson)
{
return true;
}
bool CreateUVector4(const Value& uint4ParamJson)
{
return true;
}
bool CreateFloat(const Value& floatParamJson)
{
return true;
}
bool CreateVector2(const Value& vec2ParamJson)
{
return true;
}
bool CreateVector3(const Value& vec3ParamJson)
{
return true;
}
bool CreateVector4(const Value& vec4ParamJson)
{
return true;
}
bool Initiate(const Value& parametersJson)
{
for (auto iter = parametersJson.MemberBegin(); iter != parametersJson.MemberEnd(); ++iter)
{
auto name = (iter->name).GetString();
const Value& parameterJson = parametersJson[name];
const char* s;
float v;
if (parameterJson.IsObject())
{
if (parameterJson.HasMember("type"))
{
s = parameterJson["type"].GetString();
}
}
}
return true;
}
protected:
std::vector<Parameter<bool>> boolParams;
std::vector<Parameter<BVector2>> bVector2Params;
std::vector<Parameter<BVector3>> bVector3Params;
std::vector<Parameter<BVector4>> bVector4Params;
std::vector<ParameterWithRange<int, int>> intParams;
std::vector<ParameterWithRange<IVector2, int>> iVector2Params;
std::vector<ParameterWithRange<IVector3, int>> iVector3Params;
std::vector<ParameterWithRange<IVector4, int>> iVector4Params;
std::vector<ParameterWithRange<unsigned int, unsigned int>> uintParams;
std::vector<ParameterWithRange<IVector2, unsigned int>> uVector2Params;
std::vector<ParameterWithRange<IVector3, unsigned int>> uVector3Params;
std::vector<ParameterWithRange<IVector4, unsigned int>> uVector4Params;
std::vector<ParameterWithRange<float, float>> floatParams;
std::vector<ParameterWithRange<Vector2, float>> fVector2Params;
std::vector<ParameterWithRange<Vector3, float>> fVector3Params;
std::vector<ParameterWithRange<Vector4, float>> fVector4Params;
};
class Pass
{
public:
enum Filter
{
Nearest,
Linear,
Mipmap
};
enum Wrap
{
Clamp,
Repeat,
};
class Channel
{
public:
Channel()
: texture(nullptr)
, frameBuffer(nullptr)
, filter(Mipmap)
, wrap(Repeat)
, vFlip(true)
{
}
void Terminate()
{
texture = nullptr;
frameBuffer = nullptr;
filter = Mipmap;
wrap = Repeat;
vFlip = true;
}
Texture* texture;
FlipFrameBuffer* frameBuffer;
Filter filter;
Wrap wrap;
bool vFlip;
};
Pass()
: enabled(true)
, primitives()
, shaderProgram()
, iChannels(CHANNEL_COUNT)
//, uniformGUI()
, frameBuffer(nullptr)
{
}
virtual ~Pass()
{
Terminate();
}
bool Initiate(const rapidjson::Value& json)
{
float vertices[] =
{
1.0f, 1.0f, 0.0f, // top right
1.0f, -1.0f, 0.0f, // bottom right
-1.0f, -1.0f, 0.0f, // bottom left
-1.0f, -1.0f, 0.0f, // bottom left
1.0f, 1.0f, 0.0f, // top right
-1.0f, 1.0f, 0.0f // top left
};
bool success =
primitives
.Begin()
.FillVertices(0, 3, VertexAttribute::DataType::FLOAT, false, 0, 0, vertices, sizeof(vertices) / sizeof(vertices[0]) / 3)
.End();
if (!success)
{
return false;
}
return true;
}
bool Update(unsigned int width, unsigned height, double time, double deltaTime, Vector4 mouse, Vector2 mouseDelta, int frameCounter)
{
//int facecount = 1;
Vector3 resolution = Vector3(Platform::GetWidth(), Platform::GetHeight(), 1.0);
if (frameBuffer)
{
//if (frameBuffer->GetColorAttachment(GL_COLOR_ATTACHMENT0)->GetType() == GL_TEXTURE_CUBE_MAP)
//facecount = 6;
frameBuffer->Bind();
unsigned int w;
unsigned int h;
unsigned int d;
frameBuffer->GetColorAttachment(FrameBuffer::ColorAttachment::COLOR_ATTACHMENT0)->GetResolution(&w, &h, &d);
resolution[0] = w;
resolution[1] = h;
resolution[2] = d;
renderStates.viewportState.pos = Vector2(0, 0);
renderStates.viewportState.size = Vector2(resolution[0], resolution[1]);
renderStates.scissorTestState.enabled = true;
renderStates.scissorTestState.pos = Vector2(0, 0);
renderStates.scissorTestState.size = Vector2(resolution[0], resolution[1]);
}
else
{
renderStates.viewportState.pos = Vector2(0, 0);
renderStates.viewportState.size = Vector2(width, height);
renderStates.scissorTestState.enabled = true;
renderStates.scissorTestState.pos = Vector2(0, 0);
renderStates.scissorTestState.size = Vector2(width, height);
}
shaderProgram.Bind();
shaderProgram.SetUniform3f("iResolution", resolution[0], resolution[1], resolution[2]);
shaderProgram.SetUniform1f("iTime", (float)time);
shaderProgram.SetUniform1f("iTimeDelta", (float)deltaTime);
shaderProgram.SetUniform1f("iFrameRate", 60.0f);
shaderProgram.SetUniform1i("iFrame", frameCounter);
shaderProgram.SetUniform4f("iMouse", mouse.X(), mouse.Y(), mouse.Z(), mouse.W());
//Debug("%f %f %f %f\n", mouse.X(), mouse.Y(), mouse.Z(), mouse.W());
SYSTEMTIME lt = { 0 };
GetLocalTime(<);
shaderProgram.SetUniform4f("iDate", (float)lt.wYear - 1, (float)lt.wMonth - 1, (float)lt.wDay, lt.wHour * 60.0f * 60.0f + lt.wMinute * 60.0f + lt.wSecond);
shaderProgram.SetUniform1f("iSampleRate", 48000.0);
std::vector<Vector3> channelResolutions(CHANNEL_COUNT);
std::vector<float> channelTimes(CHANNEL_COUNT);
for (int i = 0; i < iChannels.size(); i++)
{
Texture* texture = nullptr;
if (iChannels[i].texture)
texture = iChannels[i].texture;
else if (iChannels[i].frameBuffer)
texture = iChannels[i].frameBuffer->GetCurrentTexture();
if (texture)
{
unsigned int w, h, d;
texture->GetResolution(&w, &h, &d);
channelResolutions[i].X() = w;
channelResolutions[i].Y() = h;
channelResolutions[i].Z() = d;
channelTimes[i] = 0.0;
if (iChannels[i].wrap == Pass::Wrap::Repeat)
{
texture->SetWarpS(Texture::Wrap::Repeat);
texture->SetWarpR(Texture::Wrap::Repeat);
texture->SetWarpT(Texture::Wrap::Repeat);
}
else if (iChannels[i].wrap == Pass::Wrap::Clamp)
{
texture->SetWarpS(Texture::Wrap::Clamp);
texture->SetWarpR(Texture::Wrap::Clamp);
texture->SetWarpT(Texture::Wrap::Clamp);
}
if (iChannels[i].filter == Pass::Filter::Nearest)
{
texture->SetMinFilter(Texture::MinFilter::Nearest);
texture->SetMagFilter(Texture::MagFilter::Nearest);
}
else if (iChannels[i].filter == Pass::Filter::Linear)
{
texture->SetMinFilter(Texture::MinFilter::Linear);
texture->SetMagFilter(Texture::MagFilter::Linear);
}
else if (iChannels[i].filter == Pass::Filter::Mipmap)
{
texture->SetMinFilter(Texture::MinFilter::LinearMipmapLinear);
texture->SetMagFilter(Texture::MagFilter::Linear);
}
texture->Bind(i);
}
else
{
channelResolutions[i] = Vector3(0.0, 0.0, 0.0);
channelTimes[i] = 0.0;
}
}
shaderProgram.SetUniform3fv("iChannelResolution", CHANNEL_COUNT, &channelResolutions[0][0]);
shaderProgram.SetUniform1fv("iChannelTime", CHANNEL_COUNT, &channelTimes[0]);
for (int i = 0; i < CHANNEL_COUNT; i++)
{
std::string name = "iChannel";
name += ('0' + i);
shaderProgram.SetUniform1i(name.c_str(), i);
}
//uniformGUI.Update(shaderProgram);
primitives.Bind();
primitives.DrawArray(Primitives::Mode::TRIANGLES, 0, primitives.GetCount());
if (frameBuffer)
{
frameBuffer->UnBind();
}
return true;
}
void Flip()
{
if (frameBuffer)
{
frameBuffer->Flip();
}
}
void Terminate()
{
enabled = false;
renderStates.Terminate();
shaderProgram.Terminate();
for (auto& channel : iChannels)
{
channel.Terminate();
}
frameBuffer = nullptr;
primitives.Terminate();
}
void SetEnabled(bool enabled_)
{
this->enabled = enabled_;
}
bool GetEnabled() const
{
return enabled;
}
void SetFilter(int i, Filter filter_)
{
this->iChannels[i].filter = filter_;
}
void SetWrap(int i, Wrap wrap_)
{
this->iChannels[i].wrap = wrap_;
}
void SetVFlip(int i, bool vFlip_)
{
this->iChannels[i].vFlip = vFlip_;
}
void SetChannelTexture(int i, Texture* texture_)
{
this->iChannels[i].texture = texture_;
this->iChannels[i].frameBuffer = nullptr;
}
void SetChannelFrameBuffer(int i, FlipFrameBuffer* frameBuffer_)
{
this->iChannels[i].texture = nullptr;
this->iChannels[i].frameBuffer = frameBuffer_;
}
const Pass::Channel& GetChannel(int i) const
{
return this->iChannels[i];
}
void SetFrameBuffer(FlipFrameBuffer* frameBuffer_)
{
frameBuffer = frameBuffer_;
}
FlipFrameBuffer* GetFrameBuffer() const
{
return frameBuffer;
}
bool LoadShader(const char* path_, std::string& fShaderCode)
{
// 1. retrieve the vertex/fragment source code from filePath
std::ifstream fShaderFile;
// ensure ifstream objects can throw exceptions:
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
// open files
fShaderFile.open(path_);
std::stringstream fShaderStream;
// read file's buffer contents into streams
fShaderStream << fShaderFile.rdbuf();
// close file handlers
fShaderFile.close();
// convert stream into string
fShaderCode = fShaderStream.str();
}
catch (std::ifstream::failure&)
{
return false;
}
return true;
}
bool SetShader(const char* folder_, const char* fShaderURL_, const char* commonShaderURL_)
{
std::string vShaderCode =
"#version 330 core\n"
"layout(location = 0) in vec3 aPos;\n"
"out vec2 fragCoord;\n"
"uniform vec3 iResolution;\n"
"uniform float iTime;\n"
"uniform float iTimeDelta;\n"
"uniform int iFrame;\n"
"uniform float iChannelTime[4];\n"
"uniform vec4 iMouse;\n"
"uniform vec4 iDate;\n"
"uniform float iSampleRate;\n"
"uniform vec3 iChannelResolution[4];\n"
"void main()\n"
"{\n"
"gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"fragCoord = (vec2(aPos.x, aPos.y) + 1.0) / 2.0 * iResolution.xy;\n"
"}\n";
std::string fShaderHeader =
"#version 330 core\n"
"precision highp float;\n"
"precision highp int;\n"
"in vec2 fragCoord;\n"
"out vec4 FragColor;\n"
"uniform vec3 iResolution;\n"
"uniform float iTime;\n"
"uniform float iTimeDelta;\n"
"uniform int iFrame;\n"
"uniform float iChannelTime[4];\n"
"uniform vec4 iMouse;\n"
"uniform vec4 iDate;\n"
"uniform float iSampleRate;\n"
"uniform vec3 iChannelResolution[4];\n";
std::string fShaderChannels = "";
for (int i = 0; i < iChannels.size(); i++)
{
std::string idx = "0";
idx[0] += i;
if (iChannels[i].texture)
{
if (iChannels[i].texture->GetType() == Texture::Type::Texture2D)
fShaderChannels += "uniform sampler2D iChannel" + idx + ";\n";
else if (iChannels[i].texture->GetType() == Texture::Type::TextureCubeMap)
fShaderChannels += "uniform samplerCube iChannel" + idx + ";\n";
}
else if (iChannels[i].frameBuffer)
{
if (iChannels[i].frameBuffer->GetCurrentTexture()->GetType() == Texture::Type::Texture2D)
fShaderChannels += "uniform sampler2D iChannel" + idx + ";\n";
else if (iChannels[i].frameBuffer->GetCurrentTexture()->GetType() == Texture::Type::TextureCubeMap)
fShaderChannels += "uniform samplerCube iChannel" + idx + ";\n";
}
}
std::string fShaderMain =
"void main()\n"
"{\n"
"vec4 fragColor; \n"
"mainImage(fragColor, fragCoord);\n"
"FragColor = fragColor;\n"
"}\n";
std::string fShaderCode;
if (fShaderURL_)
{
std::string url;
url = folder_;
url += "/";
url += fShaderURL_;
if (!LoadShader(url.c_str(), fShaderCode))
{
Debug("failed to load shader\n", url.c_str());
return false;
}
}
std::string commonShaderCode;
if (commonShaderURL_)
{
std::string url;
url = folder_;
url += "/";
url += commonShaderURL_;
if (!LoadShader(url.c_str(), commonShaderCode))
{
Debug("failed to load shader\n", url.c_str());
return false;
}
}
std::string fShader = fShaderHeader + "\n" +
fShaderChannels + "\n" +
commonShaderCode + "\n" +
fShaderCode + "\n" +
fShaderMain;
{
FILE* fptr = fopen("test.log", "wt");
if (fptr)
{
fprintf(fptr, "%s\n", fShader.c_str());
fclose(fptr);
}
}
return shaderProgram.CreateFromSource(vShaderCode.c_str(), fShader.c_str());
}
protected:
private:
public:
protected:
private:
bool enabled;
RenderStates renderStates;
ShaderProgram shaderProgram;
std::vector<Channel> iChannels;
FlipFrameBuffer* frameBuffer;
Primitives primitives;
};
class MacShaderDemo
{
public:
MacShaderDemo()
: textures()
, black()
, soundFrameBuffer()
, bufferAFrameBuffer()
, bufferBFrameBuffer()
, bufferCFrameBuffer()
, bufferDFrameBuffer()
, cubeMapAFrameBuffer()
, cubeMapBFrameBuffer()
, cubeMapCFrameBuffer()
, cubeMapDFrameBuffer()
, passes()
{
}
virtual ~MacShaderDemo()
{
}
std::string ToLower(const char* str)
{
std::string result = str;
for (int i = 0; i < result.size(); i++)
{
result[i] = tolower(result[i]);
}
return result;
}
Pass::Filter GetFilter(const char* str)
{
std::string s = ToLower(str);
if (s == "nearest")
{
return Pass::Filter::Nearest;
}
else if (s == "linear")
{
return Pass::Filter::Linear;
}
else// if (s == "mipmap")
{
return Pass::Filter::Mipmap;
}
}
Pass::Wrap GetWrap(const char* str)
{
std::string s = ToLower(str);
if (s == "clamp")
{
return Pass::Wrap::Clamp;
}
else// if (s == "repeat")
{
return Pass::Wrap::Repeat;
}
}
FlipFrameBuffer* GetFrameBuffer(const char* str)
{
std::string s = ToLower(str);
if (s == "sound")
{
return &soundFrameBuffer;
}
else if (s == "buffera")
{
return &bufferAFrameBuffer;
}
else if (s == "bufferb")
{
return &bufferBFrameBuffer;
}
else if (s == "bufferc")
{
return &bufferCFrameBuffer;
}
else if (s == "bufferd")
{
return &bufferDFrameBuffer;
}
else if (s == "cubemapa")
{
return &cubeMapAFrameBuffer;
}
else if (s == "cubemapb")
{
return &cubeMapBFrameBuffer;
}
else if (s == "cubemapc")
{
return &cubeMapCFrameBuffer;
}
else if (s == "cubemapd")
{
return &cubeMapDFrameBuffer;
}
else
return nullptr;
}
Texture* AddKeyboardTexture()
{
KeyboardTexture* texture = new KeyboardTexture();
if (!texture)
return nullptr;
if (!texture->Initiate())
{
delete texture;
return nullptr;
}
textures.push_back(texture);
return texture;
}
Texture* AddWebcamTexture(bool vflip_)
{
WebcamTexture* texture = new WebcamTexture();
if (!texture)
return nullptr;
if (!texture->Initiate(vflip_))
{
delete texture;
return nullptr;
}
textures.push_back(texture);
return texture;
}
Texture* AddMicrophoneTexture()
{
MicrophoneTexture* texture = new MicrophoneTexture();
if (!texture)
return nullptr;
if (!texture->Initiate())
{
delete texture;
return nullptr;
}
textures.push_back(texture);
return texture;
}
Texture* AddSoundCloudTexture(const char* url_, bool vflip_)
{
SoundCloudTexture* texture = new SoundCloudTexture();
if (!texture)
return nullptr;
if (!texture->Initiate(url_, vflip_))
{
delete texture;
return nullptr;
}
textures.push_back(texture);
return texture;
}
Texture* AddTexture2D(const char* path, bool vflip_)
{
Texture2DFile* texture = new Texture2DFile();
if (!texture)
return nullptr;
if (!texture->Initiate(path, vflip_))
{
delete texture;
return nullptr;
}
textures.push_back(texture);
return texture;
}
Texture* AddTextureCubemap(const char* path, bool vflip_)
{
TextureCubeMapFile* texture = new TextureCubeMapFile();
if (!texture)
return nullptr;
if (!texture->Initiate(path, vflip_))
{
delete texture;
return nullptr;
}
textures.push_back(texture);
return texture;
}
Texture* AddVideoTexture(const char* path, bool vflip_)
{
VideoTexture* texture = new VideoTexture();
if (!texture)
return nullptr;
if (!texture->Initiate(path, vflip_))
{
delete texture;
return nullptr;
}
textures.push_back(texture);
return texture;
}
void DebugLog(const char* msg)
{
Debug("Error: %s\n", msg);
}
bool Initiate(const char* folder_)
{
if (!InitiateScene(folder_, "scene.json"))
return false;
return true;
}
bool InitiateScene(const char* folder_, const char* scenefile_)
{
std::vector<char> colors(32 * 32 * 4);
memset(&colors[0], 0, (32 * 32 * 4));
if (!black.Initiate(32, 32, 4, Texture::DynamicRange::LOW, &colors[0]))
return false;
if (!soundFrameBuffer.Initiate(512, 2, 1, Texture::DynamicRange::LOW))
return false;
if (!bufferAFrameBuffer.Initiate(Platform::GetWidth(), Platform::GetHeight(), 4, Texture::DynamicRange::HIGH))
return false;
if (!bufferBFrameBuffer.Initiate(Platform::GetWidth(), Platform::GetHeight(), 4, Texture::DynamicRange::HIGH))
return false;
if (!bufferCFrameBuffer.Initiate(Platform::GetWidth(), Platform::GetHeight(), 4, Texture::DynamicRange::HIGH))
return false;
if (!bufferDFrameBuffer.Initiate(Platform::GetWidth(), Platform::GetHeight(), 4, Texture::DynamicRange::HIGH))
return false;
if (!cubeMapAFrameBuffer.Initiate(1024, 4, Texture::DynamicRange::HIGH))
return false;
if (!cubeMapBFrameBuffer.Initiate(1024, 4, Texture::DynamicRange::HIGH))
return false;
if (!cubeMapCFrameBuffer.Initiate(1024, 4, Texture::DynamicRange::HIGH))
return false;
if (!cubeMapDFrameBuffer.Initiate(1024, 4, Texture::DynamicRange::HIGH))
return false;
std::string folder = folder_;
// 1. retrieve the vertex/fragment source code from filePath
std::string url;
std::string shaderToyCode;
std::ifstream shaderToyFile;
// ensure ifstream objects can throw exceptions:
shaderToyFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
url = folder;
url += "/";
url += scenefile_;
// open files
shaderToyFile.open(url.c_str());
std::stringstream shaderToyStream;
// read file's buffer contents into streams
shaderToyStream << shaderToyFile.rdbuf();
// close file handlers
shaderToyFile.close();
// convert stream into string
shaderToyCode = shaderToyStream.str();
}
catch (std::ifstream::failure&)
{
Debug("Failed to Open %s\n", url.c_str());
return false;
}
Document shaderToyDoc;
shaderToyDoc.Parse(shaderToyCode.c_str());
const char* commonShaderURL = CreateCommon(shaderToyDoc);
std::string customUniform;
if (!CreateParameters(shaderToyDoc))
return false;
if (!CreatePasses(shaderToyDoc, folder, commonShaderURL))
return false;
return true;
}
const char* CreateCommon(rapidjson::Document& shaderToyDoc)
{
if (shaderToyDoc.HasMember("common"))
{
Value& commonsJson = shaderToyDoc["common"];
if (commonsJson.HasMember("shader"))
{
return commonsJson["shader"].GetString();
}
}
return nullptr;
}
bool CreateParameters(rapidjson::Document& shaderToyDoc)
{
if (shaderToyDoc.HasMember("parameters"))
{
Value& parametersJson = shaderToyDoc["parameters"];
if (parametersJson.IsObject())
{
if (!parameters.Initiate(parametersJson))
return false;
}
}
return true;
}
bool CreatePasses(rapidjson::Document& shaderToyDoc, std::string& folder, const char* commonShaderURL)
{
if (shaderToyDoc.HasMember("passes"))
{
Value& passesJson = shaderToyDoc["passes"];
if (passesJson.IsArray())
{
passes.resize(passesJson.Size());
for (int i = 0; i < passes.size(); i++)
{
if (!CreatePass(i, passesJson, folder, commonShaderURL))
return false;
}
}
}
return true;
}
bool CreatePass(int i, rapidjson::Value& passesJson, std::string& folder, const char* commonShaderURL)
{
if (!passes[i].Initiate(passesJson[i]))
{
Debug("Failed to Create Pass %d\n", i);
return false;
}
Value& passJson = passesJson[i];
if (passJson.IsObject())
{
if (passJson.HasMember("rendertarget"))
{
std::string rendertargetname = passJson["rendertarget"].GetString();
if (rendertargetname != "image")
{
FlipFrameBuffer* rendertarget = this->GetFrameBuffer(rendertargetname.c_str());
if (!rendertarget)
{
Debug("rendertarget=%s not supported\n", rendertargetname.c_str());
return false;
}
passes[i].SetFrameBuffer(rendertarget);
}
else
{
passes[i].SetFrameBuffer(nullptr);
}
}
else
{
Debug("Pass must have Render Target\n");
return false;
}
for (int j = 0; j < CHANNEL_COUNT; j++)
{
passes[i].SetChannelTexture(j, &black);
std::string name = "ichannel";
name += ('0' + j);
if (passJson.HasMember(name.c_str()))
{
Value& channelJson = passJson[name.c_str()];
if (channelJson.IsObject())
{
if (channelJson.HasMember("filter"))
{
Pass::Filter channelFilter = GetFilter(channelJson["filter"].GetString());
passes[i].SetFilter(j, channelFilter);
}
if (channelJson.HasMember("wrap"))
{
Pass::Wrap channelWrap = GetWrap(channelJson["wrap"].GetString());
passes[i].SetWrap(j, channelWrap);
}
if (channelJson.HasMember("vflip"))
{
bool vFlip = channelJson["vflip"].GetBool();
passes[i].SetVFlip(j, vFlip);
}
if (channelJson.HasMember("keyboard"))
{
Texture* texture = AddKeyboardTexture();
if (!texture)
return false;
passes[i].SetChannelTexture(j, texture);
}
else if (channelJson.HasMember("webcam"))
{
Texture* texture = AddWebcamTexture(passes[i].GetChannel(j).vFlip);
if (!texture)
return false;
passes[i].SetChannelTexture(j, texture);
}
else if (channelJson.HasMember("microphone"))
{
Texture* texture = AddMicrophoneTexture();
if (!texture)
return false;
passes[i].SetChannelTexture(j, texture);
}
else if (channelJson.HasMember("soundcloud"))
{
std::string url = channelJson["soundcloud"].GetString();
Texture* texture = AddSoundCloudTexture(url.c_str(), passes[i].GetChannel(j).vFlip);
if (!texture)
{
Debug("channel %d: failed to load soundcloud %s\n", j, url.c_str());
return false;
}
passes[i].SetChannelTexture(j, texture);
}
else if (channelJson.HasMember("texture2d"))
{
if (!LoadTexture2D(folder, channelJson, i, j))
return false;
}
else if (channelJson.HasMember("texturecubemap"))
{
std::string url = folder;
url += "/";
url += channelJson["texturecubemap"].GetString();
Texture* texture = AddTextureCubemap(url.c_str(), passes[i].GetChannel(j).vFlip);
if (!texture)
{
Debug("channel %d: failed to load texturecubemap %s\n", j, url.c_str());
return false;
}
passes[i].SetChannelTexture(j, texture);
}
else if (channelJson.HasMember("texturevideo"))
{
std::string url = folder;
url += "/";
url += channelJson["texturevideo"].GetString();
Texture* texture = AddVideoTexture(url.c_str(), passes[i].GetChannel(j).vFlip);
if (!texture)
{
Debug("channel %d: failed to load texture video %s\n", j, url.c_str());
return false;
}
passes[i].SetChannelTexture(j, texture);
}
else if (channelJson.HasMember("buffer"))
{
std::string buffername = channelJson["buffer"].GetString();
FlipFrameBuffer* fb = GetFrameBuffer(buffername.c_str());
if (!fb)
{
Debug("channel %d: buffer=%s is not supported\n", j, buffername.c_str());
return false;
}
passes[i].SetChannelFrameBuffer(j, fb);
}
else
{
Debug("channel%d: must have texture or frame buffer specified or texture type is not supported\n", j);
return false;
}
}
}
}
if (passJson.HasMember("shader"))
{
const char* shaderURL = passJson["shader"].GetString();
if (!passes[i].SetShader(folder.c_str(), shaderURL, commonShaderURL))
{
return false;
}
}
else
{
Debug("channel%d: must have shader specified\n", i);
}
}
return true;
}
bool LoadTexture2D(std::string& folder, rapidjson::Value& channelJson, int i, int j)
{
std::string url = folder;
url += "/";
url += channelJson["texture2d"].GetString();
Texture* texture = AddTexture2D(url.c_str(), passes[i].GetChannel(j).vFlip);
if (!texture)
{
Debug("channel %d: failed to load texture2d %s\n", j, url.c_str());
return false;
}
passes[i].SetChannelTexture(j, texture);
return true;
}
bool Update(unsigned int width, unsigned int height, double time, double deltaTime, Vector4 mouse, Vector2 mouseDelta, int frameCounter)
{
for (auto& pass : passes)
{
if (!pass.Update(width, height, time, deltaTime, mouse, mouseDelta, frameCounter))
return false;
pass.Flip();
}
return true;
}
void Terminate()
{
for (auto texture : textures)
{
if (texture)
{
texture->Terminate();
delete texture;
texture = nullptr;
}
}
textures.clear();
black.Terminate();
soundFrameBuffer.Terminate();
bufferAFrameBuffer.Terminate();
bufferBFrameBuffer.Terminate();
bufferCFrameBuffer.Terminate();
bufferDFrameBuffer.Terminate();
cubeMapAFrameBuffer.Terminate();
cubeMapBFrameBuffer.Terminate();
cubeMapCFrameBuffer.Terminate();
cubeMapDFrameBuffer.Terminate();
}
protected:
private:
std::vector<Texture*> textures;
Texture2D black;
FlipTexture2DFrameBuffer soundFrameBuffer;
FlipTexture2DFrameBuffer bufferAFrameBuffer;
FlipTexture2DFrameBuffer bufferBFrameBuffer;
FlipTexture2DFrameBuffer bufferCFrameBuffer;
FlipTexture2DFrameBuffer bufferDFrameBuffer;
FlipTextureCubeMapFrameBuffer cubeMapAFrameBuffer;
FlipTextureCubeMapFrameBuffer cubeMapBFrameBuffer;
FlipTextureCubeMapFrameBuffer cubeMapCFrameBuffer;
FlipTextureCubeMapFrameBuffer cubeMapDFrameBuffer;
std::vector<Pass> passes;
Parameters parameters;
};