光栅化(Rasterization)是计算机图形学中一种将矢量图形转换为栅格图形(即像素)的过程。 它的主要任务是将 3D 场景中的几何对象(如三角形)映射到 2D 显示器的像素网格。光栅化通常是图形渲染管线 的一部分,在场景渲染时执行。
两个点可以确定一条直线。一个点加一个斜率也可以表示。
y-y1 y1-y2
---- = ----- = k
x-x1 x1-y2
很尴尬此方程不能表达垂线,先忽略这个问题。
效果
源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>光栅化 直线</title>
</head>
<body>
<div class="centered">
<canvas
id="canvas"
width="600"
height="600"
style="border: 1px grey solid"
></canvas>
</div>
<script>
"use strict";
let canvas = document.getElementById("canvas");
let canvas_context = canvas.getContext("2d");
let canvas_buffer = canvas_context.getImageData(
0,
0,
.width,
canvas.height
canvas;
)
// 创建一个color对象
function Color(r, g, b) {
return { r, g, b };
}
// The PutPixel() function.
function PutPixel(x, y, color) {
= canvas.width / 2 + (x | 0);
x = canvas.height / 2 - (y | 0) - 1;
y
if (x < 0 || x >= canvas.width || y < 0 || y >= canvas.height) {
return;
}
let offset = 4 * (x + canvas_buffer.width * y);
.data[offset++] = color.r;
canvas_buffer.data[offset++] = color.g;
canvas_buffer.data[offset++] = color.b;
canvas_buffer.data[offset++] = 255; // Alpha = 255 (full opacity)
canvas_buffer
}
// Displays the contents of the offscreen buffer into the canvas.
function UpdateCanvas() {
.putImageData(canvas_buffer, 0, 0);
canvas_context
}
// ======================================================================
// Rasterization code.
// ======================================================================
function DrawLineBroken(x0, y0, x1, y1, color) {
let a = (y1 - y0) / (x1 - x0);
let y = y0;
for (let x = x0; x <= x1; x++) {
PutPixel(x, y, color);
+= a;
y
}
}
DrawLineBroken(-200, -100, 240, 120, new Color(0, 0, 0));
DrawLineBroken(-50, -200, 60, 240, new Color(0, 0, 0));
UpdateCanvas();
</script>
</body>
</html>
增加对水平直线和垂直直线的支持
效果
源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>光栅化 绘制任意斜率直线</title>
</head>
<body>
<div class="centered">
<canvas
id="canvas"
width="600"
height="600"
style="border: 1px grey solid"
></canvas>
</div>
<script>
"use strict";
let canvas = document.getElementById("canvas");
let canvas_context = canvas.getContext("2d");
let canvas_buffer = canvas_context.getImageData(
0,
0,
.width,
canvas.height
canvas;
)
// 创建一个color对象
function Color(r, g, b) {
return { r, g, b };
}
// The PutPixel() function.
function PutPixel(x, y, color) {
= canvas.width / 2 + (x | 0);
x = canvas.height / 2 - (y | 0) - 1;
y
if (x < 0 || x >= canvas.width || y < 0 || y >= canvas.height) {
return;
}
let offset = 4 * (x + canvas_buffer.width * y);
.data[offset++] = color.r;
canvas_buffer.data[offset++] = color.g;
canvas_buffer.data[offset++] = color.b;
canvas_buffer.data[offset++] = 255; // Alpha = 255 (full opacity)
canvas_buffer
}
// Displays the contents of the offscreen buffer into the canvas.
function UpdateCanvas() {
.putImageData(canvas_buffer, 0, 0);
canvas_context
}
// ======================================================================
// Rasterization code.
// ======================================================================
function DrawLineBroken(x0, y0, x1, y1, color, oneStep) {
let dx = x1 - x0;
let dy = y1 - y0;
let swap = () => {
let tmp = x0;
= x1;
x0 = tmp;
x1 = y0;
tmp = y1;
y0 = tmp;
y1 ;
}
// 直线偏向水平情况
if (Math.abs(dx) > Math.abs(dy)) {
// 确保x0 < x1
if (x0 > x1) {
swap();
}let k = dy / dx; // 斜率
let y = y0;
for (let x = x0; x <= x1; x += oneStep) {
PutPixel(x, y, color);
+= k * oneStep;
y
}else {
} // 直线偏向垂直情况
if (y0 > y1) {
swap();
}let k = dx / dy;
let x = x0;
for (let y = y0; y <= y1; y += oneStep) {
PutPixel(x, y, color);
+= k * oneStep;
x
}
}
}
DrawLineBroken(-200, -100, 240, 120, new Color(0, 0, 0), 1);
DrawLineBroken(-50, -200, 60, 240, new Color(0, 0, 0), 1);
DrawLineBroken(-50, -200, -50, 240, new Color(255, 0, 0), 1);
DrawLineBroken(-50, -200, 200, -200, new Color(0, 0, 255), 1);
UpdateCanvas();
</script>
</body>
</html>
下面直线算法不是最好的或最快的,这可能会让人感到惊讶。目前比较好的直线算法可能是布兰森汉姆算法(Bresenham’s algorithm)。下面的只是为了容易理解,其实效果也还不错。
下面算法考虑了一个点的情况,从点P1到点P1的直线。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>光栅化 直线插值</title>
</head>
<body>
<div class="centered">
<canvas id="canvas" width=600 height=600 style="border: 1px grey solid"></canvas>
</div>
<script>
"use strict";
let canvas = document.getElementById("canvas");
let canvas_context = canvas.getContext("2d");
let canvas_buffer = canvas_context.getImageData(0, 0, canvas.width, canvas.height);
// 创建Color对象
function Color(r, g, b) {
return { r, g, b };
}
// 画像素点
function PutPixel(x, y, color) {
= canvas.width / 2 + (x | 0);
x = canvas.height / 2 - (y | 0) - 1;
y
if (x < 0 || x >= canvas.width || y < 0 || y >= canvas.height) {
return;
}
let offset = 4 * (x + canvas_buffer.width * y);
.data[offset++] = color.r;
canvas_buffer.data[offset++] = color.g;
canvas_buffer.data[offset++] = color.b;
canvas_buffer.data[offset++] = 255; // Alpha = 255 (full opacity)
canvas_buffer
}
// 将缓冲内容更新到画布显示
function UpdateCanvas() {
.putImageData(canvas_buffer, 0, 0);
canvas_context
}
// 点对象
function Pt(x, y) {
return { x, y };
}
function Interpolate(i0, d0, i1, d1) {
if (i0 == i1) {
return [d0]; // 一个点的情况
}
let values = [];
let a = (d1 - d0) / (i1 - i0); // 斜率
let d = d0;
for (let i = i0; i <= i1; i++) {
.push(d);
values+= a;
d
}
return values;
}
function DrawLine(p0, p1, color) {
let dx = p1.x - p0.x, dy = p1.y - p0.y;
if (Math.abs(dx) > Math.abs(dy)) { // 更趋向于水平
// 即p1.x<p0.x
if (dx < 0) { let swap = p0; p0 = p1; p1 = swap; }
// Compute the Y values and draw.
let ys = Interpolate(p0.x, p0.y, p1.x, p1.y);
for (let x = p0.x; x <= p1.x; x++) {
PutPixel(x, ys[(x - p0.x) | 0], color);
}
else {
} // The line is verical-ish. Make sure it's bottom to top.
if (dy < 0) { let swap = p0; p0 = p1; p1 = swap; }
// Compute the X values and draw.
let xs = Interpolate(p0.y, p0.x, p1.y, p1.x);
for (let y = p0.y; y <= p1.y; y++) {
PutPixel(xs[(y - p0.y) | 0], y, color);
}
}
}
DrawLine(new Pt(-200, -100), new Pt(240, 120), new Color(0, 0, 0));
DrawLine(new Pt(-50, -200), new Pt(60, 240), new Color(0, 0, 0));
DrawLine(new Pt(60, -200), new Pt(60, 240), new Color(255, 0, 0));
DrawLine(new Pt(-160, 240), new Pt(60, 240), new Color(0, 255, 0));
DrawLine(new Pt(20, 20), new Pt(20, 20), new Color(0, 0, 255));
UpdateCanvas();
</script>
</body>
</html>