1 //
2 // Copyright © 2012 Linaro Limited
3 //
4 // This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
5 //
6 // glmark2 is free software: you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the Free Software
8 // Foundation, either version 3 of the License, or (at your option) any later
9 // version.
10 //
11 // glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
12 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 // details.
15 //
16 // You should have received a copy of the GNU General Public License along with
17 // glmark2. If not, see <http://www.gnu.org/licenses/>.
18 //
19 // Authors:
20 // Jesse Barker
21 //
22 #include "scene.h"
23 #include "model.h"
24 #include "options.h"
25 #include "util.h"
26 #include "log.h"
27 #include "shader-source.h"
28 #include "stack.h"
29
30 using std::string;
31 using std::vector;
32 using std::map;
33 using LibMatrix::Stack4;
34 using LibMatrix::mat4;
35 using LibMatrix::vec4;
36 using LibMatrix::vec3;
37 using LibMatrix::vec2;
38
39 static const vec4 lightPosition(0.0f, 3.0f, 2.0f, 1.0f);
40
41 //
42 // To create a shadow map, we need a framebuffer object set up for a
43 // depth-only pass. The render target can then be bound as a texture,
44 // and the depth values sampled from that texture can be used in the
45 // distance-from-light computations when rendering the shadow on the
46 // ground below the rendered object.
47 //
48 class DepthRenderTarget
49 {
50 Program program_;
51 unsigned int canvas_width_;
52 unsigned int canvas_height_;
53 unsigned int width_;
54 unsigned int height_;
55 unsigned int tex_;
56 unsigned int fbo_;
57 unsigned int canvas_fbo_;
58 public:
DepthRenderTarget()59 DepthRenderTarget() :
60 canvas_width_(0),
61 canvas_height_(0),
62 width_(0),
63 height_(0),
64 tex_(0),
65 fbo_(0) {}
~DepthRenderTarget()66 ~DepthRenderTarget() {}
67 bool setup(unsigned int canvas_fbo, unsigned int width, unsigned int height);
68 void teardown();
69 void enable(const mat4& mvp);
70 void disable();
texture()71 unsigned int texture() { return tex_; }
program()72 Program& program() { return program_; }
73 };
74
75 bool
setup(unsigned int canvas_fbo,unsigned int width,unsigned int height)76 DepthRenderTarget::setup(unsigned int canvas_fbo, unsigned int width, unsigned int height)
77 {
78 static const string vtx_shader_filename(Options::data_path + "/shaders/depth.vert");
79 static const string frg_shader_filename(Options::data_path + "/shaders/depth.frag");
80
81 ShaderSource vtx_source(vtx_shader_filename);
82 ShaderSource frg_source(frg_shader_filename);
83
84 if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) {
85 return false;
86 }
87
88 canvas_width_ = width;
89 canvas_height_ = height;
90 canvas_fbo_ = canvas_fbo;
91 width_ = canvas_width_ * 2;
92 height_ = canvas_height_ * 2;
93
94 // If the texture will be too large for the implemnetation, we need to
95 // clamp the dimensions but maintain the aspect ratio.
96 GLint tex_size(0);
97 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &tex_size);
98 unsigned int max_size = static_cast<unsigned int>(tex_size);
99 if (max_size < width_ || max_size < height_) {
100 float aspect = static_cast<float>(width) / static_cast<float>(height);
101 width_ = max_size;
102 height_ = width_ / aspect;
103 Log::debug("DepthRenderTarget::setup: original texture size (%u x %u), clamped to (%u x %u)\n",
104 canvas_width_ * 2, canvas_height_ * 2, width_, height_);
105 }
106
107 glGenTextures(1, &tex_);
108 glBindTexture(GL_TEXTURE_2D, tex_);
109 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
110 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
111 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
112 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
113 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width_, height_, 0,
114 GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
115 glBindTexture(GL_TEXTURE_2D, 0);
116
117 GLExtensions::GenFramebuffers(1, &fbo_);
118 GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, fbo_);
119 GLExtensions::FramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
120 tex_, 0);
121 unsigned int status = GLExtensions::CheckFramebufferStatus(GL_FRAMEBUFFER);
122 if (status != GL_FRAMEBUFFER_COMPLETE) {
123 Log::error("DepthRenderTarget::setup: glCheckFramebufferStatus failed (0x%x)\n", status);
124 return false;
125 }
126 GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, canvas_fbo_);
127
128 return true;
129 }
130
131 void
teardown()132 DepthRenderTarget::teardown()
133 {
134 program_.stop();
135 program_.release();
136 if (tex_) {
137 glDeleteTextures(1, &tex_);
138 tex_ = 0;
139 }
140 if (fbo_) {
141 GLExtensions::DeleteFramebuffers(1, &fbo_);
142 fbo_ = 0;
143 }
144 }
145
146 void
enable(const mat4 & mvp)147 DepthRenderTarget::enable(const mat4& mvp)
148 {
149 program_.start();
150 program_["ModelViewProjectionMatrix"] = mvp;
151 GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, fbo_);
152 GLExtensions::FramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
153 tex_, 0);
154 glViewport(0, 0, width_, height_);
155 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
156 glClear(GL_DEPTH_BUFFER_BIT);
157 }
158
disable()159 void DepthRenderTarget::disable()
160 {
161 GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, canvas_fbo_);
162 glViewport(0, 0, canvas_width_, canvas_height_);
163 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
164 }
165
166 //
167 // The actual shadow pass is really just a quad projected into the scene
168 // with the horse's shadow cast upon it. In the vertex stage, we compute
169 // a texture coordinate for the depth texture look-up by transforming the
170 // current vertex position using a matrix describing the light's view point.
171 // In the fragment stage, that coordinate is perspective corrected, and
172 // used to sample the depth texture. If the depth value for that fragment
173 // (effectively the distance from the light to the object at that point)
174 // is less than the Z component of that coordinate (effectively the distance
175 // from the light to the ground at that point) then that location is in shadow.
176 //
177 class GroundRenderer
178 {
179 Program program_;
180 mat4 light_;
181 Stack4 modelview_;
182 mat4 projection_;
183 int positionLocation_;
184 unsigned int bufferObject_;
185 unsigned int texture_;
186 vector<vec2> vertices_;
187 vector<vec2> texcoords_;
188
189 public:
GroundRenderer()190 GroundRenderer() :
191 positionLocation_(0),
192 bufferObject_(0) {}
~GroundRenderer()193 ~GroundRenderer() {}
194 bool setup(const mat4& projection, unsigned int texture);
195 void teardown();
196 void draw();
197 };
198
199 bool
setup(const mat4 & projection,unsigned int texture)200 GroundRenderer::setup(const mat4& projection, unsigned int texture)
201 {
202 projection_ = projection;
203 texture_ = texture;
204
205 // Program set up
206 static const vec4 materialDiffuse(0.3f, 0.3f, 0.3f, 1.0f);
207 static const string vtx_shader_filename(Options::data_path + "/shaders/shadow.vert");
208 static const string frg_shader_filename(Options::data_path + "/shaders/shadow.frag");
209 ShaderSource vtx_source(vtx_shader_filename);
210 ShaderSource frg_source(frg_shader_filename);
211
212 vtx_source.add_const("MaterialDiffuse", materialDiffuse);
213
214 if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) {
215 return false;
216 }
217 positionLocation_ = program_["position"].location();
218
219 // Set up the position data for our "quad".
220 vertices_.push_back(vec2(-1.0, -1.0));
221 vertices_.push_back(vec2(1.0, -1.0));
222 vertices_.push_back(vec2(-1.0, 1.0));
223 vertices_.push_back(vec2(1.0, 1.0));
224
225 // Set up the VBO and stash our position data in it.
226 glGenBuffers(1, &bufferObject_);
227 glBindBuffer(GL_ARRAY_BUFFER, bufferObject_);
228 glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(vec2),
229 &vertices_.front(), GL_STATIC_DRAW);
230 glBindBuffer(GL_ARRAY_BUFFER, 0);
231
232 // Set up the light matrix with a bias that will convert values
233 // in the range of [-1, 1] to [0, 1)], then add in the projection
234 // and the "look at" matrix from the light position.
235 light_ *= LibMatrix::Mat4::translate(0.5, 0.5, 0.5);
236 light_ *= LibMatrix::Mat4::scale(0.5, 0.5, 0.5);
237 light_ *= projection_;
238 light_ *= LibMatrix::Mat4::lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(),
239 0.0, 0.0, 0.0,
240 0.0, 1.0, 0.0);
241
242 return true;
243 }
244
245 void
teardown()246 GroundRenderer::teardown()
247 {
248 program_.stop();
249 program_.release();
250 glBindBuffer(GL_ARRAY_BUFFER, 0);
251 glDeleteBuffers(1, &bufferObject_);
252 bufferObject_ = 0;
253 texture_= 0;
254 vertices_.clear();
255 }
256
257 void
draw()258 GroundRenderer::draw()
259 {
260 // Need to add uniforms for the shadow texture, and transformation to
261 // "lay the quad down".
262 modelview_.push();
263 modelview_.translate(projection_[0][3], projection_[1][3] - 0.8, projection_[2][3] - 0.5);
264 modelview_.rotate(-85.0, 1.0, 0.0, 0.0);
265 modelview_.scale(2.0, 2.0, 2.0);
266 mat4 mvp(projection_);
267 mvp *= modelview_.getCurrent();
268
269 glBindBuffer(GL_ARRAY_BUFFER, bufferObject_);
270 program_.start();
271 glActiveTexture(GL_TEXTURE0);
272 glBindTexture(GL_TEXTURE_2D, texture_);
273 program_["ShadowMap"] = 0;
274 program_["LightMatrix"] = light_;
275 program_["ModelViewProjectionMatrix"] = mvp;
276
277 glEnableVertexAttribArray(positionLocation_);
278 glVertexAttribPointer(positionLocation_, 2, GL_FLOAT, GL_FALSE, 0, 0);
279 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
280 glDisableVertexAttribArray(positionLocation_);
281 glBindTexture(GL_TEXTURE_2D, 0);
282 glBindBuffer(GL_ARRAY_BUFFER, 0);
283 modelview_.pop();
284 }
285
286 class ShadowPrivate
287 {
288 Canvas& canvas_;
289 DepthRenderTarget depthTarget_;
290 GroundRenderer ground_;
291 Program program_;
292 Stack4 modelview_;
293 Stack4 projection_;
294 Mesh mesh_;
295 vec3 centerVec_;
296 float radius_;
297 float rotation_;
298 float rotationSpeed_;
299 bool useVbo_;
300
301 public:
ShadowPrivate(Canvas & canvas)302 ShadowPrivate(Canvas& canvas) :
303 canvas_(canvas),
304 radius_(0.0),
305 rotation_(0.0),
306 rotationSpeed_(36.0),
307 useVbo_(true) {}
~ShadowPrivate()308 ~ShadowPrivate() {}
309
310 bool setup(map<string, Scene::Option>& options);
311 void teardown();
312 void update(double elapsedTime);
313 void draw();
314 };
315
316 bool
setup(map<string,Scene::Option> & options)317 ShadowPrivate::setup(map<string, Scene::Option>& options)
318 {
319 // Program object setup
320 static const string vtx_shader_filename(Options::data_path + "/shaders/light-basic.vert");
321 static const string frg_shader_filename(Options::data_path + "/shaders/light-basic.frag");
322 static const vec4 materialDiffuse(1.0f, 1.0f, 1.0f, 1.0f);
323
324 ShaderSource vtx_source(vtx_shader_filename);
325 ShaderSource frg_source(frg_shader_filename);
326
327 vtx_source.add_const("LightSourcePosition", lightPosition);
328 vtx_source.add_const("MaterialDiffuse", materialDiffuse);
329
330 if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) {
331 return false;
332 }
333
334 // Model setup
335 Model::find_models();
336 Model model;
337 bool modelLoaded = model.load("horse");
338
339 if(!modelLoaded) {
340 return false;
341 }
342
343 if (model.needNormals())
344 model.calculate_normals();
345
346 // Mesh setup
347 vector<std::pair<Model::AttribType, int> > attribs;
348 attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypePosition, 3));
349 attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeNormal, 3));
350 model.convert_to_mesh(mesh_, attribs);
351
352 useVbo_ = (options["use-vbo"].value == "true");
353 bool interleave = (options["interleave"].value == "true");
354 mesh_.vbo_update_method(Mesh::VBOUpdateMethodMap);
355 mesh_.interleave(interleave);
356
357 if (useVbo_) {
358 mesh_.build_vbo();
359 }
360 else {
361 mesh_.build_array();
362 }
363
364 // Calculate a projection matrix that is a good fit for the model
365 vec3 maxVec = model.maxVec();
366 vec3 minVec = model.minVec();
367 vec3 diffVec = maxVec - minVec;
368 centerVec_ = maxVec + minVec;
369 centerVec_ /= 2.0;
370 float diameter = diffVec.length();
371 radius_ = diameter / 2;
372 float fovy = 2.0 * atanf(radius_ / (2.0 + radius_));
373 fovy /= M_PI;
374 fovy *= 180.0;
375 float aspect(static_cast<float>(canvas_.width())/static_cast<float>(canvas_.height()));
376 projection_.perspective(fovy, aspect, 2.0, 50.0);
377
378 if (!depthTarget_.setup(canvas_.fbo(), canvas_.width(), canvas_.height())) {
379 Log::error("Failed to set up the render target for the depth pass\n");
380 return false;
381 }
382
383 if (!ground_.setup(projection_.getCurrent(), depthTarget_.texture())) {
384 Log::error("Failed to set up the ground renderer\n");
385 return false;
386 }
387
388 return true;
389 }
390
391
392 void
teardown()393 ShadowPrivate::teardown()
394 {
395 depthTarget_.teardown();
396 ground_.teardown();
397 program_.stop();
398 program_.release();
399 mesh_.reset();
400 }
401
402 void
update(double elapsedTime)403 ShadowPrivate::update(double elapsedTime)
404 {
405 rotation_ = rotationSpeed_ * elapsedTime;
406 }
407
408 void
draw()409 ShadowPrivate::draw()
410 {
411 // To perform the depth pass, set up the model-view transformation so
412 // that we're looking at the horse from the light position. That will
413 // give us the appropriate view for the shadow.
414 modelview_.push();
415 modelview_.loadIdentity();
416 modelview_.lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(),
417 0.0, 0.0, 0.0,
418 0.0, 1.0, 0.0);
419 modelview_.rotate(rotation_, 0.0f, 1.0f, 0.0f);
420 mat4 mvp(projection_.getCurrent());
421 mvp *= modelview_.getCurrent();
422 modelview_.pop();
423
424 // Enable the depth render target with our transformation and render.
425 depthTarget_.enable(mvp);
426 vector<GLint> attrib_locations;
427 attrib_locations.push_back(depthTarget_.program()["position"].location());
428 attrib_locations.push_back(depthTarget_.program()["normal"].location());
429 mesh_.set_attrib_locations(attrib_locations);
430 if (useVbo_) {
431 mesh_.render_vbo();
432 }
433 else {
434 mesh_.render_array();
435 }
436 depthTarget_.disable();
437
438 // Ground rendering using the above generated texture...
439 ground_.draw();
440
441 // Draw the "normal" view of the horse
442 modelview_.push();
443 modelview_.translate(-centerVec_.x(), -centerVec_.y(), -(centerVec_.z() + 2.0 + radius_));
444 modelview_.rotate(rotation_, 0.0f, 1.0f, 0.0f);
445 mvp = projection_.getCurrent();
446 mvp *= modelview_.getCurrent();
447
448 program_.start();
449 program_["ModelViewProjectionMatrix"] = mvp;
450
451 // Load the NormalMatrix uniform in the shader. The NormalMatrix is the
452 // inverse transpose of the model view matrix.
453 LibMatrix::mat4 normal_matrix(modelview_.getCurrent());
454 normal_matrix.inverse().transpose();
455 program_["NormalMatrix"] = normal_matrix;
456 attrib_locations.clear();
457 attrib_locations.push_back(program_["position"].location());
458 attrib_locations.push_back(program_["normal"].location());
459 mesh_.set_attrib_locations(attrib_locations);
460 if (useVbo_) {
461 mesh_.render_vbo();
462 }
463 else {
464 mesh_.render_array();
465 }
466
467 // Per-frame cleanup
468 modelview_.pop();
469 }
470
SceneShadow(Canvas & canvas)471 SceneShadow::SceneShadow(Canvas& canvas) :
472 Scene(canvas, "shadow"),
473 priv_(0)
474 {
475 options_["use-vbo"] = Scene::Option("use-vbo", "true",
476 "Whether to use VBOs for rendering",
477 "false,true");
478 options_["interleave"] = Scene::Option("interleave", "false",
479 "Whether to interleave vertex attribute data",
480 "false,true");
481 }
482
483 bool
supported(bool show_errors)484 SceneShadow::supported(bool show_errors)
485 {
486 static const string oes_depth_texture("GL_OES_depth_texture");
487 static const string arb_depth_texture("GL_ARB_depth_texture");
488 bool ret = true;
489
490 if (!GLExtensions::support(oes_depth_texture) &&
491 !GLExtensions::support(arb_depth_texture)) {
492 if (show_errors) {
493 Log::error("We do not have the depth texture extension!!!\n");
494 }
495
496 ret = false;
497 }
498
499 if (!GLExtensions::GenFramebuffers) {
500 if (show_errors)
501 Log::error("SceneShadow requires GL framebuffer support\n");
502 ret = false;
503 }
504
505 return ret;
506 }
507
508 bool
load()509 SceneShadow::load()
510 {
511 running_ = false;
512 return true;
513 }
514
515 void
unload()516 SceneShadow::unload()
517 {
518 }
519
520 bool
setup()521 SceneShadow::setup()
522 {
523 // If the scene isn't supported, don't bother to go through setup.
524 if (!Scene::setup())
525 return false;
526
527 priv_ = new ShadowPrivate(canvas_);
528 if (!priv_->setup(options_)) {
529 delete priv_;
530 priv_ = 0;
531 return false;
532 }
533
534 // Set core scene timing after actual initialization so we don't measure
535 // set up time.
536 startTime_ = Util::get_timestamp_us() / 1000000.0;
537 lastUpdateTime_ = startTime_;
538 running_ = true;
539
540 return true;
541 }
542
543 void
teardown()544 SceneShadow::teardown()
545 {
546 // Add scene-specific teardown here
547 if (priv_) {
548 priv_->teardown();
549 delete priv_;
550 }
551 Scene::teardown();
552 }
553
554 void
update()555 SceneShadow::update()
556 {
557 Scene::update();
558 // Add scene-specific update here
559 priv_->update(lastUpdateTime_ - startTime_);
560 }
561
562 void
draw()563 SceneShadow::draw()
564 {
565 priv_->draw();
566 }
567
568 Scene::ValidationResult
validate()569 SceneShadow::validate()
570 {
571 return Scene::ValidationUnknown;
572 }
573