How to add flat shaded objects #10
-
Hello I managed to add objects on the go but the shading/texture part of this is very foreign to me, |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 11 replies
-
Hey @odarcan, shading is done using as you probably know shaders. I wrote a quick description of the shading pipeline here: https://phpgl.net/getting-started/drawing_a_triangle.html#the-rendering-pipeline My getting started guide is unfortunately not yet complete so for further details you have to consult different sources. Now for your question specifically. If you just want to be able to shade things with plain colors in a "low-poly" like style For a simple example consider this code: note: I make use of the <?php
/**
* We utilize the example helpers here to focus on what matter in this specifc example.
*/
require __DIR__ . '/99_example_helpers.php';
use GL\Math\{GLM, Vec3, Vec4, Mat4};
use GL\Buffer\FloatBuffer;
/**
* Position / Normal Vertex Array
*/
class PNVertexArray
{
public int $VAO = 0;
public int $VBO = 0;
public readonly FLoatBuffer $vertices;
public int $vertexCount;
public function __construct(FLoatBuffer $vertices)
{
$this->vertices = $vertices;
// validate the size of vertex buffer is divisible by 6
if ($vertices->size() % 6 !== 0) {
throw new \InvalidArgumentException('The vertex buffer size must be divisible by 6');
}
$this->vertexCount = $vertices->size() / 6;
// create a vertex array object and upload the vertices
glGenVertexArrays(1, $this->VAO);
glGenBuffers(1, $this->VBO);
glBindVertexArray($this->VAO);
glBindBuffer(GL_ARRAY_BUFFER, $this->VBO);
glBufferData(GL_ARRAY_BUFFER, $vertices, GL_STATIC_DRAW);
// declare the vertex attributes
// positions
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, GL_SIZEOF_FLOAT * 6, 0);
glEnableVertexAttribArray(0);
// normals
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, GL_SIZEOF_FLOAT * 6, GL_SIZEOF_FLOAT * 3);
glEnableVertexAttribArray(1);
// unbind
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
public function __destruct()
{
glDeleteBuffers(1, $this->VBO);
glDeleteVertexArrays(1, $this->VAO);
}
public function bind(): void
{
glBindVertexArray($this->VAO);
}
public function draw(): void
{
glDrawArrays(GL_TRIANGLES, 0, $this->vertexCount);
}
}
class FlatShader
{
public int $program;
public int $modelLocation;
public int $viewLocation;
public int $projectionLocation;
public int $eyeLocation;
public int $meshColorLocation;
public int $lightDirLocation;
public int $lightColorLocation;
public int $ambientLocation;
public function __construct()
{
$this->program = ExampleHelper::compileShader(<<< 'GLSL'
#version 330 core
layout (location = 0) in vec3 a_position;
layout (location = 1) in vec3 a_normal;
out vec3 v_normal;
out vec3 v_position;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
// we need to transform the normal vector to world space
v_normal = vec3(model * vec4(a_normal, 1.0f));
v_position = vec3(model * vec4(a_position, 1.0f));
gl_Position = projection * view * model * vec4(a_position, 1.0f);
}
GLSL,
<<< 'GLSL'
#version 330 core
out vec4 fragment_color;
in vec3 v_normal;
in vec3 v_position;
uniform vec3 eye; // camera position
uniform vec3 mesh_color;
uniform vec3 light_dir; // not actually a direction, but a position
uniform vec3 light_color;
uniform float ambient = 0.3;
void main()
{
// basic blinn-phong lighting
vec3 N = normalize(v_normal);
vec3 L = normalize(light_dir - v_position);
vec3 V = normalize(eye - v_position);
vec3 H = normalize(L + V);
float diffuse = max(dot(N, L), 0.0);
float specular = pow(max(dot(N, H), 0.0), 32.0);
vec3 color = (ambient + diffuse) * mesh_color + specular * light_color;
fragment_color = vec4(color, 1.0f);
}
GLSL);
$this->modelLocation = glGetUniformLocation($this->program, 'model');
$this->viewLocation = glGetUniformLocation($this->program, 'view');
$this->projectionLocation = glGetUniformLocation($this->program, 'projection');
$this->eyeLocation = glGetUniformLocation($this->program, 'eye');
$this->meshColorLocation = glGetUniformLocation($this->program, 'mesh_color');
$this->lightDirLocation = glGetUniformLocation($this->program, 'light_dir');
$this->lightColorLocation = glGetUniformLocation($this->program, 'light_color');
$this->ambientLocation = glGetUniformLocation($this->program, 'ambient');
}
public function bind(): void
{
glUseProgram($this->program);
}
}
class Model
{
public Mat4 $translation;
public PNVertexArray $mesh;
public Vec3 $color;
public function __construct(PNVertexArray $mesh)
{
$this->mesh = $mesh;
$this->translation = new Mat4;
$this->color = new Vec3(1.0, 1.0, 1.0);
}
public function draw(FlatShader $shader): void
{
$this->mesh->bind();
// update the local properties
glUniformMatrix4f($shader->modelLocation, GL_FALSE, $this->translation);
glUniformVec3f($shader->meshColorLocation, $this->color);
$this->mesh->draw();
}
}
/**
* Open up a window
*/
$window = ExampleHelper::begin();
// create a simple phong shader
$shader = new FlatShader;
// create a plane mesh
$planeVertices = new PNVertexArray(new FloatBuffer([
// positions // normals
5.0, -0.5, 5.0, 0.0, 1.0, 0.0,
-5.0, -0.5, 5.0, 0.0, 1.0, 0.0,
-5.0, -0.5, -5.0, 0.0, 1.0, 0.0,
5.0, -0.5, 5.0, 0.0, 1.0, 0.0,
-5.0, -0.5, -5.0, 0.0, 1.0, 0.0,
5.0, -0.5, -5.0, 0.0, 1.0, 0.0
]));
$cubeVertices = new PNVertexArray(new FloatBuffer([
// positions // normals
-0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
-0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
-0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
-0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
-0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
-0.5, 0.5, 0.5, -1.0, 0.0, 0.0,
-0.5, 0.5, -0.5, -1.0, 0.0, 0.0,
-0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
-0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
-0.5, -0.5, 0.5, -1.0, 0.0, 0.0,
-0.5, 0.5, 0.5, -1.0, 0.0, 0.0,
0.5, 0.5, 0.5, 1.0, 0.0, 0.0,
0.5, 0.5, -0.5, 1.0, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0, 0.0,
0.5, -0.5, 0.5, 1.0, 0.0, 0.0,
0.5, 0.5, 0.5, 1.0, 0.0, 0.0,
-0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
-0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
-0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
-0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
-0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
-0.5, 0.5, -0.5, 0.0, 1.0, 0.0
]));
// update the viewport
glViewport(0, 0, ExampleHelper::WIN_WIDTH, ExampleHelper::WIN_HEIGHT);
// enable depth testing, because we are rendering a 3d object with overlapping
// triangles
glEnable(GL_DEPTH_TEST);
// array of renderables
$ground = new Model($planeVertices);
$ground->color = new Vec3(0.0, 1.0, 0.0);
$ground->translation->translate(new Vec3(0.0, 0.0, 0.0));
$ground->translation->scale(new Vec3(10.0, 10.0, 10.0));
// array of renderables
$rendereables = [
$ground
];
// add some random cubes
for ($i = 0; $i < 100; $i++) {
$cube = new Model($cubeVertices);
$cube->translation->translate(new Vec3(
mt_rand(-1000, 1000) * 0.01,
mt_rand(0, 2),
mt_rand(-1000, 1000) * 0.01
));
$cube->translation->scale(new Vec3(
mt_rand(1, 3) * 0.5,
mt_rand(1, 3) * 0.5,
mt_rand(1, 3) * 0.5
));
$cube->color = new Vec3(
mt_rand(0, 255) / 255.0,
mt_rand(0, 255) / 255.0,
mt_rand(0, 255) / 255.0
);
$rendereables[] = $cube;
}
// capture keyboard events to toggle rendering modes
$wireframe = false;
glfwSetKeyCallback($window, function ($key, $scancode, $action, $mods) use (&$wireframe) {
if ($key == GLFW_KEY_ESCAPE && $action == GLFW_PRESS) {
glfwSetWindowShouldClose($window, true);
}
if ($key == GLFW_KEY_W && $action == GLFW_PRESS) {
$wireframe = !$wireframe;
}
});
// in this example we let the user move the camera around
// or more accurately, we rotate the object
$rotation = 0.0;
$rotationVelocity = 2.0;
$lastMousePositionX = 0.0;
$lightPosition = new Vec3(0.0, 5.0, 0.0);
// print some help to the console
echo str_repeat('-', 80) . PHP_EOL;
echo '-> Use the mouse to rotate the object' . PHP_EOL;
echo '-> Press ESC to close the window' . PHP_EOL;
echo '-> Press W to toggle wireframe mode' . PHP_EOL;
echo str_repeat('-', 80) . PHP_EOL;
// Main Loop
// ----------------------------------------------------------------------------
while (!glfwWindowShouldClose($window))
{
glClearColor(0, 0, 0, 1);
// note how we are clearing both the DEPTH and COLOR buffers.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// use the shader, will active the given shader program
// for the coming draw calls.
$shader->bind();
// update rotation velcotiy based on user input
if (glfwGetMouseButton($window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) {
glfwGetCursorPos($window, $mousePositionX, $mousePositionY);
if ($lastMousePositionX != 0.0) {
$diff = $mousePositionX - $lastMousePositionX;
$rotationVelocity = $diff * 0.01;
}
$lastMousePositionX = $mousePositionX;
} else {
$rotationVelocity *= 0.95;
if (abs($rotationVelocity) < 0.001) {
$rotationVelocity = 0.0;
}
}
// max rotation speed
$rotationVelocity = max(-1.0, min(1.0, $rotationVelocity));
$rotation += $rotationVelocity;
// continuesly rotate the light around the center
$lightPosition->x = 10.0 * sin(glfwGetTime());
$lightPosition->z = 10.0 * cos(glfwGetTime());
// next the view matrix, this is the camera / eye position and rotation
$view = new Mat4;
// use the rotation to move the camera in a circle around the center
$position = new Vec3(
10.0 * sin(GLM::radians($rotation)),
5.0,
10.0 * cos(GLM::radians($rotation))
);
$view->lookAt($position, new Vec3(0.0, 0.0, -1.0), new Vec3(0.0, 1.0, 0.0));
// and finally the projection matrix, this is the perspective matrix.
$projection = new Mat4;
$projection->perspective(GLM::radians(70.0), ExampleHelper::WIN_WIDTH / ExampleHelper::WIN_HEIGHT, 0.1, 10000.0);
// now set the uniform variables in the shader.
// note that we use `glUniformMatrix4f` instead of `glUniformMatrix4fv` to pass a single matrix.
glUniformMatrix4f($shader->viewLocation, GL_FALSE, $view);
glUniformMatrix4f($shader->projectionLocation, GL_FALSE, $projection);
glUniformVec3f($shader->eyeLocation, $position);
// set the light direction and color
glUniformVec3f($shader->lightDirLocation, $lightPosition);
glUniform3f($shader->lightColorLocation, 1.0, 1.0, 1.0);
if ($wireframe) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
} else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
foreach($rendereables as $renderable)
{
$renderable->draw($shader);
}
// swap the windows framebuffer and
// poll queued window events.
glfwSwapBuffers($window);
glfwPollEvents();
}
ExampleHelper::stop($window); |
Beta Was this translation helpful? Give feedback.
-
@mario-deluna would mesa3d work with php-glfw ? https://docs.mesa3d.org/osmesa.html maybe it could be statically compiled |
Beta Was this translation helpful? Give feedback.
Hey @odarcan, shading is done using as you probably know shaders.
I wrote a quick description of the shading pipeline here: https://phpgl.net/getting-started/drawing_a_triangle.html#the-rendering-pipeline
My getting started guide is unfortunately not yet complete so for further details you have to consult different sources.
Now for your question specifically. If you just want to be able to shade things with plain colors in a "low-poly" like style
I would go with a simple "flat shading" model or "phong shading".
For a simple example consider this code:
note: I make use of the
99_example_helpers.php
file here, also this example should provide more clarity from an OOP background but does not…