You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
770 lines
18 KiB
770 lines
18 KiB
//
|
|
// Copyright 2014 The ANGLE Project Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
//
|
|
|
|
#include "util/shader_utils.h"
|
|
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
#include "common/utilities.h"
|
|
#include "util/test_utils.h"
|
|
|
|
namespace
|
|
{
|
|
bool ReadEntireFile(const std::string &filePath, std::string *contentsOut)
|
|
{
|
|
constexpr uint32_t kMaxBufferSize = 2000;
|
|
char buffer[kMaxBufferSize] = {};
|
|
if (!angle::ReadEntireFileToString(filePath.c_str(), buffer, kMaxBufferSize) ||
|
|
strlen(buffer) == 0)
|
|
return false;
|
|
*contentsOut = buffer;
|
|
return true;
|
|
}
|
|
|
|
GLuint CompileProgramInternal(const char *vsSource,
|
|
const char *tcsSource,
|
|
const char *tesSource,
|
|
const char *gsSource,
|
|
const char *fsSource,
|
|
const std::function<void(GLuint)> &preLinkCallback)
|
|
{
|
|
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
|
|
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
|
|
|
|
if (vs == 0 || fs == 0)
|
|
{
|
|
glDeleteShader(fs);
|
|
glDeleteShader(vs);
|
|
return 0;
|
|
}
|
|
|
|
GLuint program = glCreateProgram();
|
|
|
|
glAttachShader(program, vs);
|
|
glDeleteShader(vs);
|
|
|
|
glAttachShader(program, fs);
|
|
glDeleteShader(fs);
|
|
|
|
GLuint tcs = 0;
|
|
GLuint tes = 0;
|
|
GLuint gs = 0;
|
|
|
|
if (strlen(tcsSource) > 0)
|
|
{
|
|
tcs = CompileShader(GL_TESS_CONTROL_SHADER_EXT, tcsSource);
|
|
if (tcs == 0)
|
|
{
|
|
glDeleteShader(vs);
|
|
glDeleteShader(fs);
|
|
glDeleteProgram(program);
|
|
return 0;
|
|
}
|
|
|
|
glAttachShader(program, tcs);
|
|
glDeleteShader(tcs);
|
|
}
|
|
|
|
if (strlen(tesSource) > 0)
|
|
{
|
|
tes = CompileShader(GL_TESS_EVALUATION_SHADER_EXT, tesSource);
|
|
if (tes == 0)
|
|
{
|
|
glDeleteShader(vs);
|
|
glDeleteShader(fs);
|
|
glDeleteShader(tcs);
|
|
glDeleteProgram(program);
|
|
return 0;
|
|
}
|
|
|
|
glAttachShader(program, tes);
|
|
glDeleteShader(tes);
|
|
}
|
|
|
|
if (strlen(gsSource) > 0)
|
|
{
|
|
gs = CompileShader(GL_GEOMETRY_SHADER_EXT, gsSource);
|
|
if (gs == 0)
|
|
{
|
|
glDeleteShader(vs);
|
|
glDeleteShader(fs);
|
|
glDeleteShader(tcs);
|
|
glDeleteShader(tes);
|
|
glDeleteProgram(program);
|
|
return 0;
|
|
}
|
|
|
|
glAttachShader(program, gs);
|
|
glDeleteShader(gs);
|
|
}
|
|
|
|
if (preLinkCallback)
|
|
{
|
|
preLinkCallback(program);
|
|
}
|
|
|
|
glLinkProgram(program);
|
|
|
|
return CheckLinkStatusAndReturnProgram(program, true);
|
|
}
|
|
|
|
const void *gCallbackChainUserParam;
|
|
|
|
void KHRONOS_APIENTRY DebugMessageCallback(GLenum source,
|
|
GLenum type,
|
|
GLuint id,
|
|
GLenum severity,
|
|
GLsizei length,
|
|
const GLchar *message,
|
|
const void *userParam)
|
|
{
|
|
std::string sourceText = gl::GetDebugMessageSourceString(source);
|
|
std::string typeText = gl::GetDebugMessageTypeString(type);
|
|
std::string severityText = gl::GetDebugMessageSeverityString(severity);
|
|
std::cerr << sourceText << ", " << typeText << ", " << severityText << ": " << message << "\n";
|
|
|
|
GLDEBUGPROC callbackChain = reinterpret_cast<GLDEBUGPROC>(const_cast<void *>(userParam));
|
|
if (callbackChain)
|
|
{
|
|
callbackChain(source, type, id, severity, length, message, gCallbackChainUserParam);
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
GLuint CompileShader(GLenum type, const char *source)
|
|
{
|
|
GLuint shader = glCreateShader(type);
|
|
|
|
const char *sourceArray[1] = {source};
|
|
glShaderSource(shader, 1, sourceArray, nullptr);
|
|
glCompileShader(shader);
|
|
|
|
GLint compileResult;
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
|
|
|
|
if (compileResult == 0)
|
|
{
|
|
GLint infoLogLength;
|
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
|
|
|
|
// Info log length includes the null terminator, so 1 means that the info log is an empty
|
|
// string.
|
|
if (infoLogLength > 1)
|
|
{
|
|
std::vector<GLchar> infoLog(infoLogLength);
|
|
glGetShaderInfoLog(shader, static_cast<GLsizei>(infoLog.size()), nullptr, &infoLog[0]);
|
|
std::cerr << "shader compilation failed: " << &infoLog[0];
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "shader compilation failed. <Empty log message>";
|
|
}
|
|
|
|
std::cerr << std::endl;
|
|
|
|
glDeleteShader(shader);
|
|
shader = 0;
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath)
|
|
{
|
|
std::string source;
|
|
if (!ReadEntireFile(sourcePath, &source))
|
|
{
|
|
std::cerr << "Error reading shader file: " << sourcePath << "\n";
|
|
return 0;
|
|
}
|
|
|
|
return CompileShader(type, source.c_str());
|
|
}
|
|
|
|
GLuint CheckLinkStatusAndReturnProgram(GLuint program, bool outputErrorMessages)
|
|
{
|
|
if (glGetError() != GL_NO_ERROR)
|
|
return 0;
|
|
|
|
GLint linkStatus;
|
|
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
|
|
if (linkStatus == 0)
|
|
{
|
|
if (outputErrorMessages)
|
|
{
|
|
GLint infoLogLength;
|
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
|
|
|
|
// Info log length includes the null terminator, so 1 means that the info log is an
|
|
// empty string.
|
|
if (infoLogLength > 1)
|
|
{
|
|
std::vector<GLchar> infoLog(infoLogLength);
|
|
glGetProgramInfoLog(program, static_cast<GLsizei>(infoLog.size()), nullptr,
|
|
&infoLog[0]);
|
|
|
|
std::cerr << "program link failed: " << &infoLog[0];
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "program link failed. <Empty log message>";
|
|
}
|
|
}
|
|
|
|
glDeleteProgram(program);
|
|
return 0;
|
|
}
|
|
|
|
return program;
|
|
}
|
|
|
|
GLuint GetProgramShader(GLuint program, GLint requestedType)
|
|
{
|
|
static constexpr GLsizei kMaxShaderCount = 16;
|
|
GLuint attachedShaders[kMaxShaderCount] = {0u};
|
|
GLsizei count = 0;
|
|
glGetAttachedShaders(program, kMaxShaderCount, &count, attachedShaders);
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
GLint type = 0;
|
|
glGetShaderiv(attachedShaders[i], GL_SHADER_TYPE, &type);
|
|
if (type == requestedType)
|
|
{
|
|
return attachedShaders[i];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
GLuint CompileProgramWithTransformFeedback(
|
|
const char *vsSource,
|
|
const char *fsSource,
|
|
const std::vector<std::string> &transformFeedbackVaryings,
|
|
GLenum bufferMode)
|
|
{
|
|
auto preLink = [&](GLuint program) {
|
|
if (transformFeedbackVaryings.size() > 0)
|
|
{
|
|
std::vector<const char *> constCharTFVaryings;
|
|
|
|
for (const std::string &transformFeedbackVarying : transformFeedbackVaryings)
|
|
{
|
|
constCharTFVaryings.push_back(transformFeedbackVarying.c_str());
|
|
}
|
|
|
|
glTransformFeedbackVaryings(program,
|
|
static_cast<GLsizei>(transformFeedbackVaryings.size()),
|
|
&constCharTFVaryings[0], bufferMode);
|
|
}
|
|
};
|
|
|
|
return CompileProgramInternal(vsSource, "", "", "", fsSource, preLink);
|
|
}
|
|
|
|
GLuint CompileProgram(const char *vsSource, const char *fsSource)
|
|
{
|
|
return CompileProgramInternal(vsSource, "", "", "", fsSource, nullptr);
|
|
}
|
|
|
|
GLuint CompileProgram(const char *vsSource,
|
|
const char *fsSource,
|
|
const std::function<void(GLuint)> &preLinkCallback)
|
|
{
|
|
return CompileProgramInternal(vsSource, "", "", "", fsSource, preLinkCallback);
|
|
}
|
|
|
|
GLuint CompileProgramWithGS(const char *vsSource, const char *gsSource, const char *fsSource)
|
|
{
|
|
return CompileProgramInternal(vsSource, "", "", gsSource, fsSource, nullptr);
|
|
}
|
|
|
|
GLuint CompileProgramWithTESS(const char *vsSource,
|
|
const char *tcsSource,
|
|
const char *tesSource,
|
|
const char *fsSource)
|
|
{
|
|
return CompileProgramInternal(vsSource, tcsSource, tesSource, "", fsSource, nullptr);
|
|
}
|
|
|
|
GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath)
|
|
{
|
|
std::string vsSource;
|
|
if (!ReadEntireFile(vsPath, &vsSource))
|
|
{
|
|
std::cerr << "Error reading shader: " << vsPath << "\n";
|
|
return 0;
|
|
}
|
|
|
|
std::string fsSource;
|
|
if (!ReadEntireFile(fsPath, &fsSource))
|
|
{
|
|
std::cerr << "Error reading shader: " << fsPath << "\n";
|
|
return 0;
|
|
}
|
|
|
|
return CompileProgram(vsSource.c_str(), fsSource.c_str());
|
|
}
|
|
|
|
GLuint CompileComputeProgram(const char *csSource, bool outputErrorMessages)
|
|
{
|
|
GLuint program = glCreateProgram();
|
|
|
|
GLuint cs = CompileShader(GL_COMPUTE_SHADER, csSource);
|
|
if (cs == 0)
|
|
{
|
|
glDeleteProgram(program);
|
|
return 0;
|
|
}
|
|
|
|
glAttachShader(program, cs);
|
|
|
|
glLinkProgram(program);
|
|
|
|
return CheckLinkStatusAndReturnProgram(program, outputErrorMessages);
|
|
}
|
|
|
|
GLuint LoadBinaryProgramOES(const std::vector<uint8_t> &binary, GLenum binaryFormat)
|
|
{
|
|
GLuint program = glCreateProgram();
|
|
glProgramBinaryOES(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
|
|
return CheckLinkStatusAndReturnProgram(program, true);
|
|
}
|
|
|
|
GLuint LoadBinaryProgramES3(const std::vector<uint8_t> &binary, GLenum binaryFormat)
|
|
{
|
|
GLuint program = glCreateProgram();
|
|
glProgramBinary(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
|
|
return CheckLinkStatusAndReturnProgram(program, true);
|
|
}
|
|
|
|
bool LinkAttachedProgram(GLuint program)
|
|
{
|
|
glLinkProgram(program);
|
|
return (CheckLinkStatusAndReturnProgram(program, true) != 0);
|
|
}
|
|
|
|
void EnableDebugCallback(GLDEBUGPROC callbackChain, const void *userParam)
|
|
{
|
|
gCallbackChainUserParam = userParam;
|
|
|
|
glEnable(GL_DEBUG_OUTPUT);
|
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
|
// Enable medium and high priority messages.
|
|
glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr,
|
|
GL_TRUE);
|
|
glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr,
|
|
GL_TRUE);
|
|
// Disable low and notification priority messages.
|
|
glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, nullptr,
|
|
GL_FALSE);
|
|
glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr,
|
|
GL_FALSE);
|
|
// Disable performance messages to reduce spam.
|
|
glDebugMessageControlKHR(GL_DONT_CARE, GL_DEBUG_TYPE_PERFORMANCE, GL_DONT_CARE, 0, nullptr,
|
|
GL_FALSE);
|
|
glDebugMessageCallbackKHR(DebugMessageCallback, reinterpret_cast<const void *>(callbackChain));
|
|
}
|
|
|
|
namespace angle
|
|
{
|
|
|
|
namespace essl1_shaders
|
|
{
|
|
|
|
const char *PositionAttrib()
|
|
{
|
|
return "a_position";
|
|
}
|
|
const char *ColorUniform()
|
|
{
|
|
return "u_color";
|
|
}
|
|
|
|
const char *Texture2DUniform()
|
|
{
|
|
return "u_tex2D";
|
|
}
|
|
|
|
namespace vs
|
|
{
|
|
|
|
// A shader that sets gl_Position to zero.
|
|
const char *Zero()
|
|
{
|
|
return R"(void main()
|
|
{
|
|
gl_Position = vec4(0);
|
|
})";
|
|
}
|
|
|
|
// A shader that sets gl_Position to attribute a_position.
|
|
const char *Simple()
|
|
{
|
|
return R"(precision highp float;
|
|
attribute vec4 a_position;
|
|
|
|
void main()
|
|
{
|
|
gl_Position = a_position;
|
|
})";
|
|
}
|
|
|
|
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
|
|
// v_position.
|
|
const char *Passthrough()
|
|
{
|
|
return R"(precision highp float;
|
|
attribute vec4 a_position;
|
|
varying vec4 v_position;
|
|
|
|
void main()
|
|
{
|
|
gl_Position = a_position;
|
|
v_position = a_position;
|
|
})";
|
|
}
|
|
|
|
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
|
|
// texcoord.
|
|
const char *Texture2D()
|
|
{
|
|
return R"(precision highp float;
|
|
attribute vec4 a_position;
|
|
varying vec2 v_texCoord;
|
|
|
|
void main()
|
|
{
|
|
gl_Position = vec4(a_position.xy, 0.0, 1.0);
|
|
v_texCoord = a_position.xy * 0.5 + vec2(0.5);
|
|
})";
|
|
}
|
|
|
|
} // namespace vs
|
|
|
|
namespace fs
|
|
{
|
|
|
|
// A shader that renders a simple checker pattern of red and green. X axis and y axis separate the
|
|
// different colors. Needs varying v_position.
|
|
const char *Checkered()
|
|
{
|
|
return R"(precision highp float;
|
|
varying vec4 v_position;
|
|
|
|
void main()
|
|
{
|
|
bool isLeft = v_position.x < 0.0;
|
|
bool isTop = v_position.y < 0.0;
|
|
if (isLeft)
|
|
{
|
|
if (isTop)
|
|
{
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}
|
|
else
|
|
{
|
|
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isTop)
|
|
{
|
|
gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
|
|
}
|
|
else
|
|
{
|
|
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
|
|
}
|
|
}
|
|
})";
|
|
}
|
|
|
|
// A shader that fills with color taken from uniform named "color".
|
|
const char *UniformColor()
|
|
{
|
|
return R"(uniform mediump vec4 u_color;
|
|
void main(void)
|
|
{
|
|
gl_FragColor = u_color;
|
|
})";
|
|
}
|
|
|
|
// A shader that fills with 100% opaque red.
|
|
const char *Red()
|
|
{
|
|
return R"(precision mediump float;
|
|
|
|
void main()
|
|
{
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
})";
|
|
}
|
|
|
|
// A shader that fills with 100% opaque green.
|
|
const char *Green()
|
|
{
|
|
return R"(precision mediump float;
|
|
|
|
void main()
|
|
{
|
|
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
|
})";
|
|
}
|
|
|
|
// A shader that fills with 100% opaque blue.
|
|
const char *Blue()
|
|
{
|
|
return R"(precision mediump float;
|
|
|
|
void main()
|
|
{
|
|
gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
|
|
})";
|
|
}
|
|
|
|
// A shader that samples the texture.
|
|
const char *Texture2D()
|
|
{
|
|
return R"(precision mediump float;
|
|
uniform sampler2D u_tex2D;
|
|
varying vec2 v_texCoord;
|
|
|
|
void main()
|
|
{
|
|
gl_FragColor = texture2D(u_tex2D, v_texCoord);
|
|
})";
|
|
}
|
|
|
|
} // namespace fs
|
|
} // namespace essl1_shaders
|
|
|
|
namespace essl3_shaders
|
|
{
|
|
|
|
const char *PositionAttrib()
|
|
{
|
|
return "a_position";
|
|
}
|
|
const char *Texture2DUniform()
|
|
{
|
|
return "u_tex2D";
|
|
}
|
|
const char *LodUniform()
|
|
{
|
|
return "u_lod";
|
|
}
|
|
|
|
namespace vs
|
|
{
|
|
|
|
// A shader that sets gl_Position to zero.
|
|
const char *Zero()
|
|
{
|
|
return R"(#version 300 es
|
|
void main()
|
|
{
|
|
gl_Position = vec4(0);
|
|
})";
|
|
}
|
|
|
|
// A shader that sets gl_Position to attribute a_position.
|
|
const char *Simple()
|
|
{
|
|
return R"(#version 300 es
|
|
in vec4 a_position;
|
|
void main()
|
|
{
|
|
gl_Position = a_position;
|
|
})";
|
|
}
|
|
|
|
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
|
|
// v_position.
|
|
const char *Passthrough()
|
|
{
|
|
return R"(#version 300 es
|
|
in vec4 a_position;
|
|
out vec4 v_position;
|
|
void main()
|
|
{
|
|
gl_Position = a_position;
|
|
v_position = a_position;
|
|
})";
|
|
}
|
|
|
|
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
|
|
// texcoord.
|
|
const char *Texture2DLod()
|
|
{
|
|
return R"(#version 300 es
|
|
in vec4 a_position;
|
|
out vec2 v_texCoord;
|
|
|
|
void main()
|
|
{
|
|
gl_Position = vec4(a_position.xy, 0.0, 1.0);
|
|
v_texCoord = a_position.xy * 0.5 + vec2(0.5);
|
|
})";
|
|
}
|
|
|
|
} // namespace vs
|
|
|
|
namespace fs
|
|
{
|
|
|
|
// A shader that fills with 100% opaque red.
|
|
const char *Red()
|
|
{
|
|
return R"(#version 300 es
|
|
precision highp float;
|
|
out vec4 my_FragColor;
|
|
void main()
|
|
{
|
|
my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
})";
|
|
}
|
|
|
|
// A shader that fills with 100% opaque green.
|
|
const char *Green()
|
|
{
|
|
return R"(#version 300 es
|
|
precision highp float;
|
|
out vec4 my_FragColor;
|
|
void main()
|
|
{
|
|
my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
|
})";
|
|
}
|
|
|
|
// A shader that fills with 100% opaque blue.
|
|
const char *Blue()
|
|
{
|
|
return R"(#version 300 es
|
|
precision highp float;
|
|
out vec4 my_FragColor;
|
|
void main()
|
|
{
|
|
my_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
|
|
})";
|
|
}
|
|
|
|
// A shader that samples the texture at a given lod.
|
|
const char *Texture2DLod()
|
|
{
|
|
return R"(#version 300 es
|
|
precision mediump float;
|
|
uniform sampler2D u_tex2D;
|
|
uniform float u_lod;
|
|
in vec2 v_texCoord;
|
|
out vec4 my_FragColor;
|
|
|
|
void main()
|
|
{
|
|
my_FragColor = textureLod(u_tex2D, v_texCoord, u_lod);
|
|
})";
|
|
}
|
|
|
|
} // namespace fs
|
|
} // namespace essl3_shaders
|
|
|
|
namespace essl31_shaders
|
|
{
|
|
|
|
const char *PositionAttrib()
|
|
{
|
|
return "a_position";
|
|
}
|
|
|
|
namespace vs
|
|
{
|
|
|
|
// A shader that sets gl_Position to zero.
|
|
const char *Zero()
|
|
{
|
|
return R"(#version 310 es
|
|
void main()
|
|
{
|
|
gl_Position = vec4(0);
|
|
})";
|
|
}
|
|
|
|
// A shader that sets gl_Position to attribute a_position.
|
|
const char *Simple()
|
|
{
|
|
return R"(#version 310 es
|
|
in vec4 a_position;
|
|
void main()
|
|
{
|
|
gl_Position = a_position;
|
|
})";
|
|
}
|
|
|
|
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
|
|
// v_position.
|
|
const char *Passthrough()
|
|
{
|
|
return R"(#version 310 es
|
|
in vec4 a_position;
|
|
out vec4 v_position;
|
|
void main()
|
|
{
|
|
gl_Position = a_position;
|
|
v_position = a_position;
|
|
})";
|
|
}
|
|
|
|
} // namespace vs
|
|
|
|
namespace fs
|
|
{
|
|
|
|
// A shader that fills with 100% opaque red.
|
|
const char *Red()
|
|
{
|
|
return R"(#version 310 es
|
|
precision highp float;
|
|
out vec4 my_FragColor;
|
|
void main()
|
|
{
|
|
my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
})";
|
|
}
|
|
|
|
// A shader that fills with 100% opaque green.
|
|
const char *Green()
|
|
{
|
|
return R"(#version 310 es
|
|
precision highp float;
|
|
out vec4 my_FragColor;
|
|
void main()
|
|
{
|
|
my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
|
})";
|
|
}
|
|
|
|
// A shader that renders a simple gradient of red to green. Needs varying v_position.
|
|
const char *RedGreenGradient()
|
|
{
|
|
return R"(#version 310 es
|
|
precision highp float;
|
|
in vec4 v_position;
|
|
out vec4 my_FragColor;
|
|
|
|
void main()
|
|
{
|
|
my_FragColor = vec4(v_position.xy * 0.5 + vec2(0.5), 0.0, 1.0);
|
|
})";
|
|
}
|
|
|
|
} // namespace fs
|
|
} // namespace essl31_shaders
|
|
} // namespace angle
|