blob: d23ed56fa11435281e2105b33ad363fd4095faf3 [file] [log] [blame]
/* Copyright 2018 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "shader_program.h" // NOLINT
#include "util.h" // NOLINT
namespace ndk_hello_vr_beta {
namespace {
// Multiview vertex shaders use transforms defined by arrays of mat4 uniforms,
// using gl_ViewID_OVR to determine the array index.
// Simple shaders to render .obj files without any lighting.
constexpr const char* kTexturedMeshVertexShader =
// The following shader is for multiview rendering.
R"glsl(#version 320 es
#extension GL_OVR_multiview2 : enable
layout(num_views=2) in;
uniform mat4 u_MVP[2];
in vec4 a_Position;
in vec2 a_UV;
out vec2 v_UV;
void main() {
mat4 mvp = u_MVP[gl_ViewID_OVR];
v_UV = a_UV;
gl_Position = mvp * a_Position;
})glsl";
constexpr const char* kTexturedMeshFragmentShader =
R"glsl(#version 320 es
precision mediump float;
in vec2 v_UV;
out vec4 FragColor;
uniform sampler2D u_Texture;
void main() {
// The y coordinate of this sample's textures is reversed compared to
// what OpenGL expects, so we invert the y coordinate.
FragColor = texture(u_Texture, vec2(v_UV.x, 1.0 - v_UV.y));
})glsl";
constexpr const char* kTexturedAlphaMeshFragmentShader =
R"glsl(#version 320 es
precision mediump float;
in vec2 v_UV;
out vec4 FragColor;
uniform sampler2D u_Texture;
uniform float a_Alpha;
void main() {
// The y coordinate of this sample's textures is reversed compared to
// what OpenGL expects, so we invert the y coordinate.
FragColor = texture(u_Texture, vec2(v_UV.x, 1.0 - v_UV.y));
FragColor.a = FragColor.a * a_Alpha;
})glsl";
constexpr const char* kTexturedMeshAlphaFragmentShader =
R"glsl(#version 320 es
precision mediump float;
in vec2 v_UV;
out vec4 FragColor;
uniform sampler2D u_Texture;
uniform float a_Alpha;
uniform vec4 a_BatteryUVRect;
uniform vec2 a_BatteryOffset;
// Returns true if point is inside box.
// Expects rect as xmin, ymin, xmax, ymax.
bool inRect(vec2 pt, vec4 rect) {
vec2 result = step(rect.xy, pt) - (vec2(1.0, 1.0) - step(pt, rect.zw));
return result.x * result.y > 0.5;
}
void main() {
// Explicitly choose a mip level to work around incorrect mip level at
// boundary of rectangle.
vec2 texture_coord = fwidth(v_UV) * vec2(textureSize(u_Texture, 0));
float mip_level = log2(max((texture_coord.x + texture_coord.y)*0.5, 1.0));
// If the uv is in the battery section, offset to the battery indicator.
FragColor = inRect(v_UV, a_BatteryUVRect) ?
textureLod(u_Texture, v_UV + a_BatteryOffset, mip_level) :
textureLod(u_Texture, v_UV, mip_level);
FragColor.a = FragColor.a * a_Alpha;
})glsl";
/**
* Converts a string into an OpenGL ES shader.
*
* @param type The type of shader (GL_VERTEX_SHADER or GL_FRAGMENT_SHADER).
* @param shader_source The source code of the shader.
* @return The shader object handler, or 0 if there's an error.
*/
GLuint LoadGLShader(GLenum type, const char* shader_source) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &shader_source, nullptr);
glCompileShader(shader);
// Get the compilation status.
GLint compile_status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
// If the compilation failed, delete the shader and show an error.
if (compile_status == 0) {
GLint info_len = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len);
if (info_len == 0) {
return 0;
}
std::vector<char> info_string(info_len);
glGetShaderInfoLog(shader, info_string.size(), nullptr, info_string.data());
LOGE("Could not compile shader of type %d: %s", type, info_string.data());
glDeleteShader(shader);
return 0;
} else {
return shader;
}
}
} // anonymous namespace
void ShaderProgram::Link(const char* vertex, const char* fragment) {
program_ = glCreateProgram();
const int vertex_shader = LoadGLShader(GL_VERTEX_SHADER, vertex);
const int fragment_shader = LoadGLShader(GL_FRAGMENT_SHADER, fragment);
glAttachShader(program_, vertex_shader);
glAttachShader(program_, fragment_shader);
glLinkProgram(program_);
glUseProgram(program_);
}
void TexturedShaderProgram::Link() {
ShaderProgram::Link(kTexturedMeshVertexShader, kTexturedMeshFragmentShader);
mode_view_projection_ = glGetUniformLocation(program_, "u_MVP");
}
void TexturedShaderProgram::SetModelViewProjection(
const gvr::Mat4f& model, const gvr::Mat4f view_projection[2]) const {
gvr::Mat4f model_view_projection[2] = {MatrixMul(view_projection[0], model),
MatrixMul(view_projection[1], model)};
glUniformMatrix4fv(mode_view_projection_, 2, GL_FALSE,
MatrixPairToGLArray(model_view_projection).data());
}
GLuint TexturedShaderProgram::GetPositionAttribute() const {
return glGetAttribLocation(program_, "a_Position");
}
GLuint TexturedShaderProgram::GetUVAttribute() const {
return glGetAttribLocation(program_, "a_UV");
}
void TexturedAlphaShaderProgram::Link() {
ShaderProgram::Link(kTexturedMeshVertexShader,
kTexturedAlphaMeshFragmentShader);
mode_view_projection_ = glGetUniformLocation(program_, "u_MVP");
alpha_ = glGetUniformLocation(program_, "a_Alpha");
}
void TexturedAlphaShaderProgram::SetAlpha(float alpha) const {
glUniform1f(alpha_, alpha);
}
void ControllerShaderProgram::Link() {
ShaderProgram::Link(kTexturedMeshVertexShader,
kTexturedMeshAlphaFragmentShader);
mode_view_projection_ = glGetUniformLocation(program_, "u_MVP");
alpha_ = glGetUniformLocation(program_, "a_Alpha");
battery_uv_rect_ = glGetUniformLocation(program_, "a_BatteryUVRect");
battery_offset_ = glGetUniformLocation(program_, "a_BatteryOffset");
SetAlpha(1.0f);
}
void ControllerShaderProgram::SetBatteryUVRect(const gvr::Rectf& uv) const {
glUniform4f(battery_uv_rect_, uv.left, uv.bottom, uv.right, uv.top);
}
void ControllerShaderProgram::SetBatteryOffset(const gvr::Vec2f& offset) const {
glUniform2f(battery_offset_, offset.x, offset.y);
}
} // namespace ndk_hello_vr_beta