Skip to content

Commit

Permalink
update control
Browse files Browse the repository at this point in the history
  • Loading branch information
shi-yan committed Aug 2, 2024
1 parent f1264b5 commit 150a017
Show file tree
Hide file tree
Showing 6 changed files with 5 additions and 5 deletions.
Binary file added Control/arcball.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions Control/arcball_camera_control.html
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ <h2 >3.1 Arcball Camera Control</h2><p>another common interaction we want to hav
this.currentRotation = glMatrix.mat4.create();
}
...
}</code></pre><p>let me explain the members, radius is the distance between the focal point or the rotation center and the camera. The forward vector defines viewing direction from the camera's position to the focal point. The forward vector is initialized by (radius, 0.0, 0.0), meaning the camera is initialized at the (radius, 0.0, 0.0) and looking at the origin. The up vector is the direction perpendicular to the forward vector and point towards the up direction of the camera.</p><p>The current rotation is the rotation matrix of our Arcball.</p><pre><code class="language-javascript code-block"> class Arcball {
}</code></pre><p>let me explain the members, radius is the distance between the focal point or the rotation center and the camera. The forward vector defines viewing direction from the camera's position to the focal point. The forward vector is initialized by (radius, 0.0, 0.0), meaning the camera is initialized at the (radius, 0.0, 0.0) and looking at the origin. The up vector is the direction perpendicular to the forward vector and point towards the up direction of the camera.</p><p><div class="img-container"><img class="img" onclick="openImage(this)" src="thumb_arcball.png" original_src="arcball.png" alt="The Arcball" sources='[]' /><div class="img-title">The Arcball</div></div></p><p>The current rotation is the rotation matrix of our Arcball.</p><pre><code class="language-javascript code-block"> class Arcball {
...
yawPitch(originalX, originalY, currentX, currentY) {

Expand All @@ -177,7 +177,7 @@ <h2 >3.1 Arcball Camera Control</h2><p>another common interaction we want to hav
}
}
...
}</code></pre><p>the first function we look at controls the yaw and pitch. this function takes two inputs, the original vector is the original location in the screen space. And the current is the current mouse position in the screen space. because our camera was initially positioned at (radius, 0, 0), the plane that parallel to the screen is the yz plane. The first step is projecting the original and current points in the screen space to the yz plane: (1.0, originalX, originalY), (1.0, currentX, currentY). notice that we are not using the coordinates (radius, originalX, originalY) and (radius, currentX, currentY), because we want to simplify our calculation by treating our immaginary sphere to have a radius of 1. Even the actual radius is not necessary 1. This doesn't affect the correctness, as we basically perform our calculation in the sphere's coordinate system.</p><p>Next, we need to calculate the axis around which we will apply the rotation. This axis is perpendicular to the plane formed by the original and the current vectors. Hence we get it by performing a cross product of the two vectors.</p><p>the above calculation is assuming there is no previous rotation in the system, hence the yz plane is still aligned with the screen plane. If there has been previous rotations, we need to also apply the previous rotations to the rotation axis. and finally we normalize this rotation axis to a unit vector.</p><p>next, we need to obtain the rotation angle. which is derived by first getting the sin value and an arcsin. Since the maximum rotation angle is less than 90 degrees.</p><p>finally, we will calculate the rotation matrix by using the axis and the rotation angle. in the code, we need to check the null of the rotation matrix, because when the original and the current vectors are very close, we might get an invalid rotation matrix due to numerical instability.</p><p>if we actually have a valid rotation matrix, we then merge the exisiting rotation with the new rotation matrix and use them to rotate the camera's forward and up vectors.</p><pre><code class="language-javascript code-block">
}</code></pre><p>the first function we look at controls the yaw and pitch. this function takes two inputs, the original vector is the original location in the screen space. And the current is the current mouse position in the screen space. because our camera was initially positioned at (radius, 0, 0), the plane that parallel to the screen is the yz plane. The first step is projecting the original and current points in the screen space to the yz plane: (1.0, originalX, originalY), (1.0, currentX, currentY). notice that we are not using the coordinates (radius, originalX, originalY) and (radius, currentX, currentY), because we want to simplify our calculation by treating our imaginary sphere to have a radius of 1. Even the actual radius is not necessary 1. This doesn't affect the correctness, as we basically perform our calculation in the sphere's coordinate system.</p><p>Next, we need to calculate the axis around which we will apply the rotation. This axis is perpendicular to the plane formed by the original and the current vectors. Hence we get it by performing a cross product of the two vectors.</p><p>the above calculation is assuming there is no previous rotation in the system, hence the yz plane is still aligned with the screen plane. If there has been previous rotations, we need to also apply the previous rotations to the rotation axis. and finally we normalize this rotation axis to a unit vector.</p><p>next, we need to obtain the rotation angle. which is derived by first getting the sin value and an arcsin. Since the maximum rotation angle is less than 90 degrees.</p><p>finally, we will calculate the rotation matrix by using the axis and the rotation angle. in the code, we need to check the null of the rotation matrix, because when the original and the current vectors are very close, we might get an invalid rotation matrix due to numerical instability.</p><p>if we actually have a valid rotation matrix, we then merge the exisiting rotation with the new rotation matrix and use them to rotate the camera's forward and up vectors.</p><pre><code class="language-javascript code-block">

class Arcball {
...
Expand Down
2 changes: 1 addition & 1 deletion Control/object_picking.html
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ <h2 >3.2 Object Picking</h2><p>Another important way of interaction is picking o
teapot.selectionUniformBuffer, 0, 4);
device.queue.submit([commandEncoder.finish()]);
requestAnimationFrame(render);
}</code></pre><h3 >Color coding</h3><p>Ray casting is not difficult to implement, but it won't fit every scenario. For example, the proxy geometry is not as fine as the actual object. Sometimes, we want precise picking. second, ray casting we implemented doesn't consider occlussion. a further away and occoluded object can still be picked if it happens to be visited first when we iterate through all objects. Sometimes, certain objects have a single side. for example, consider a plane that has only one front side. When this plane is facing away from us, it should not be visible if we cull its back face. we shouldn't pick it when it is not visible, but ray picking will picking it still. Third, when there are a lot of objects, ray casting might be slow, especially when most objects are not visible due to occolusion or outside the view. Of course, this situation can be improved by adopting a better scene datastructure, such as the kd tree, which allows us to only test ray hit against geometry objects that have the potential to intersect the ray.</p><p>In this second section, we will discuss an alternative picking solution called color coding that can solve some of the issues mentioned. The principle behind color coding is also not difficult. we encode each object's id as a unique color. For RGB color, we can encode 2^24 objects, which should be enough for most of the use cases. We then render the scene using these false color and read back what we have rendered. For this step, we need to be careful by not enabling any process that can potentially alter the color values, such as blending and anti-aliasing. Once the rendering is read back, we recover objects' id from the colors and see what color is currently under the mouse cursor. we don't have to read the whole image, but just a small patch under the mouse should be enough and also we can render the scene not necessary in the full resolution, but a smaller resolution to improve performance, as picking is often not required to be pixel level accurate.</p><p>The benefit of color coding is obvious. By rendering the scene, only the front most and front facing object is checked for selection. There is also no coarse proxy object, so we can do precise picking using the actual geometry. but unlike the ray casting approach, the color coding implementation requires a shader code and the javascript code to work jointly.</p><p>Now let's look at the code:</p><pre><code> @vertex
}</code></pre><h3 >Color coding</h3><p>Ray casting is not difficult to implement, but it won't fit every scenario. For example, the proxy geometry is not as fine as the actual object. Sometimes, we want precise picking. second, ray casting we implemented doesn't consider occlusion. a further away and occluded object can still be picked if it happens to be visited first when we iterate through all objects. Sometimes, certain objects have a single side. for example, consider a plane that has only one front side. When this plane is facing away from us, it should not be visible if we cull its back face. we shouldn't pick it when it is not visible, but ray picking will picking it still. Third, when there are a lot of objects, ray casting might be slow, especially when most objects are not visible due to occlusion or outside the view. Of course, this situation can be improved by adopting a better scene datastructure, such as the kd tree, which allows us to only test ray hit against geometry objects that have the potential to intersect the ray.</p><p>In this second section, we will discuss an alternative picking solution called color coding that can solve some of the issues mentioned. The principle behind color coding is also not difficult. we encode each object's id as a unique color. For RGB color, we can encode 2^24 objects, which should be enough for most of the use cases. We then render the scene using these false color and read back what we have rendered. For this step, we need to be careful by not enabling any process that can potentially alter the color values, such as blending and anti-aliasing. Once the rendering is read back, we recover objects' id from the colors and see what color is currently under the mouse cursor. we don't have to read the whole image, but just a small patch under the mouse should be enough and also we can render the scene not necessary in the full resolution, but a smaller resolution to improve performance, as picking is often not required to be pixel level accurate.</p><p>The benefit of color coding is obvious. By rendering the scene, only the front most and front facing object is checked for selection. There is also no coarse proxy object, so we can do precise picking using the actual geometry. but unlike the ray casting approach, the color coding implementation requires a shader code and the javascript code to work jointly.</p><p>Now let's look at the code:</p><pre><code> @vertex
fn vs_main(
@builtin(instance_index) instanceIdx : u32,
@location(0) inPos: vec3&lt;f32&gt;,
Expand Down
2 changes: 1 addition & 1 deletion Control/saving_images_and_videos.html
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ <h2 >3.3 Saving Images and Videos</h2><p>In this tutorial, we are going to look
bitrate: 2_000_000,
latencyMode: 'realtime',
framerate: 25
});</code></pre><p>finally, we create the encoder and configure it. the encoder takes two callbacks. The output callback is triggered when a frame is encoded. we simply pass the data chunk to the webmWriter to assemble the video file. and the error callback is called upon errors.</p><p>to configure the encoder, we specify vp9 for codec, bitrate and framerate etc. and finally we set the frameCount to zero and get the high precision timestamp. we primarly use the frameCount to decide when to encode a key frame and when to encode a intra frame. One keyframes, a video codec often only employ spacial compression. whereas on intra frames, a video codec employs timproal compression also. intra frames have a higher compression rate, but they can't be decompressed by only looking at themselves. we have to find the previous key frame and all intra frames between this keyframe and the current frame to decode it. because compression error can accumulate, therefore every several intra frames, we need to encode a key frame. unlike an intra frame, a keyframe can be decoded by itself. but a keyframe is large in size than an intra frame.</p><p>encoding a frame also requires a timestamp, therefore we record the time when video recording begins.</p><pre><code class="language-javascript code-block"> frameCount = 0;
});</code></pre><p>finally, we create the encoder and configure it. the encoder takes two callbacks. The output callback is triggered when a frame is encoded. we simply pass the data chunk to the webmWriter to assemble the video file. and the error callback is called upon errors.</p><p>to configure the encoder, we specify vp9 for codec, bitrate and framerate etc. and finally we set the frameCount to zero and get the high precision timestamp. we primarily use the frameCount to decide when to encode a key frame and when to encode a intra frame. One keyframes, a video codec often only employ spacial compression. whereas on intra frames, a video codec employs temporal compression also. intra frames have a higher compression rate, but they can't be decompressed by only looking at themselves. we have to find the previous key frame and all intra frames between this keyframe and the current frame to decode it. because compression error can accumulate, therefore every several intra frames, we need to encode a key frame. unlike an intra frame, a keyframe can be decoded by itself. but a keyframe is large in size than an intra frame.</p><p>encoding a frame also requires a timestamp, therefore we record the time when video recording begins.</p><pre><code class="language-javascript code-block"> frameCount = 0;
beginTime = window.performance.now();</code></pre><p>similar to the image dumping code, for video encoding, we also put the code after a frame is rendered. here if the encoder has been initialized, we first obtain the current time. and we provide the canvas and the elapsed time since the beginning to a VideoFrame. we call the encode function and for every 10 frames, we encode a key frame.</p><pre><code class="language-javascript code-block">
function render() {
...
Expand Down
Binary file added Control/thumb_arcball.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion code/2_04_text/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
let texCoordsBuffer = createGPUBuffer(device, texCoords, GPUBufferUsage.VERTEX);

let translateMatrix = glMatrix.mat4.lookAt(glMatrix.mat4.create(),
glMatrix.vec3.fromValues(0, 0, 100), glMatrix.vec3.fromValues(0, 0, 0), glMatrix.vec3.fromValues(0.0, 1.0, 0.0));
glMatrix.vec3.fromValues(0, 0, 800), glMatrix.vec3.fromValues(0, 0, 0), glMatrix.vec3.fromValues(0.0, 1.0, 0.0));

let projectionMatrix = glMatrix.mat4.perspective(glMatrix.mat4.create(),
1.4, 640.0 / 480.0, 0.1, 1000.0);
Expand Down

0 comments on commit 150a017

Please sign in to comment.