• C++模拟OpenGL库——图形光栅化理论及实现(二):Brensenham直线算法


    目录

    理论

    实现:画一条线

    实现:画一圈线

    实现——画一圈彩色线:


    理论

    已知两点P1 P2,来推导直线算法

    如:y=kx+b

    但其中会产生浮点数,我们知道在像素级别的绘制中是不能出现浮点数的,因此我们需要想办法消灭浮点数。

    先说结论:Brensenham直线算法就是不断判断下图中d1和d2的距离,取其中的最小值来判断要绘制哪一个像素。

    下面进行一些数学的推导:

    由图中:

    d_1=k(x_i+1)+b-y_i

    d2=yi+1(k(xi+1)+b)" role="presentation">d2=yi+1(k(xi+1)+b)

    \therefore d_1-d_2=2k(x_i+1)-2y_i+2b-1

    \because k=dy/dx

    \because dy=\frac{P_2.y-P_1.y}{P_2.x-P_1.x}(其中产生了浮点数)

    \therefore P_i=dx(d_1-d_2)=2dy\cdot x_i-2dx\cdot y_i+(2dy+2b\cdot dx-dx)

    其中为了方便设(2dy+2b\cdot dx-dx)=c

    Pi+1=2dy(xi+1)2dx(yi+1)+c" role="presentation">Pi+1=2dy(xi+1)2dx(yi+1)+c

    Pi+1Pi=2dy2dx(yi+1yi)" role="presentation">Pi+1Pi=2dy2dx(yi+1yi)

    考察第一个点:

    P_1(x_1,kx_{1}+b)

    带入P_i=dx(d_1-d_2)=2dy\cdot x_i-2dx\cdot y_i+(2dy+2b\cdot dx-dx)

    得:P_1=2dy-dx

    由此可以判断它的正负,若为负,说明d1-d2<0,即d1

    通过P_1可以迭代P_2

    P2=P1+2dy2dx(y2y1)=P1+2dy2dx;(P1>0,y2=y1+1)" role="presentation">P2=P1+2dy2dx(y2y1)=P1+2dy2dx;(P1>0,y2=y1+1)

    P2=P1+2dy2dx(y2y1)=P1+2dy;(P1<0,y2=y1)" role="presentation">P2=P1+2dy2dx(y2y1)=P1+2dy;(P1<0,y2=y1)

    整体的思路,就是说通过P1这个点,来消除浮点数的出现,然后判断P1的正负,来选择下一个像素是上面的还是下面的像素(看上面的图)

    总结:

    初始:P1=2ΔyΔx" role="presentation">P1=2ΔyΔx

    Pi≥=0" role="presentation">Pi≥=0(也就是d1>d2,选择上面的像素点):

    • yi+1=yi+1" role="presentation">yi+1=yi+1
    • x_{i+1}=x_i+1
    • P_{i+1}=P_i+2(\Delta y-\Delta x)

    否则(也就是d1<d2,选择下面的像素点):

    • yi+1=yi" role="presentation">yi+1=yi
    • x_{i+1}=x_i+1
    • Pi+1=Pi+2Δy" role="presentation">Pi+1=Pi+2Δy

    --------------------------------------------以上是在斜率k<1的情况下-------------------------------------------------

    如果k>1,则将dx与dy进行交换,用y进行”步进“

    实现:画一条线

    1. namespace GT {
    2. void GT::Canvas::drawLine(intV2 pt1, intV2 pt2, RGBA _color){
    3. int disX = abs(pt2.x - pt1.x);
    4. int disY = abs(pt2.y - pt1.y);
    5. int xNow = pt1.x;
    6. int yNow = pt2.y;
    7. int stepX = 0;
    8. int stepY = 0;
    9. //判断两个方向步进的正负
    10. stepX = pt1.x < pt2.x ? 1 : -1;
    11. stepY = pt1.y < pt2.y ? 1 : -1;
    12. //对比xy偏移量,决定步进方向选取x or y
    13. int sumStep = disX;
    14. bool useXStep = true;
    15. if (disX < disY) {
    16. sumStep = disY;
    17. useXStep = false;
    18. SWAP_INT(disX, disY);
    19. }
    20. //初始化P
    21. int p = 2 * disY - disX;
    22. for (int i = 0; i < sumStep; i++) {
    23. drawPoint(xNow, yNow, _color);
    24. if (p >= 0) {
    25. if (useXStep) {
    26. yNow += stepY;
    27. }
    28. else {
    29. xNow += stepX;
    30. }
    31. p = p - 2 * disX;
    32. }
    33. //步进主坐标
    34. if (useXStep) {
    35. xNow += stepX;
    36. }
    37. else {
    38. yNow += stepY;
    39. }
    40. p = p + 2 * disY;
    41. }
    42. }
    43. }

    测试:

    1. void Render() {
    2. _canvas->clear();
    3. 逐像素绘制
    4. //for (int i = 0; i < wWidth; i++) {
    5. // for (int j = 0; j < wHeight; j++) {
    6. // GT::RGBA _color(rand() % 255, rand() % 255, rand() % 255);
    7. // _canvas->drawPoint(i, j, _color);
    8. // }
    9. //}
    10. _canvas->drawLine(GT::intV2(100, 100), GT::intV2(150, 150),GT::RGBA(255,0,0));
    11. //在这里画到设备上,hMem相当于缓冲区
    12. BitBlt(hDC, 0, 0, wWidth, wHeight, hMem, 0, 0, SRCCOPY);
    13. }

    实现:画一圈线

    1. void Render() {
    2. _canvas->clear();
    3. GT::RGBA _color(rand() % 255, rand() % 255, 0);
    4. GT::intV2 pt1(100, 100);
    5. float r = 50;
    6. for (int i = 0; i < 360; i += 20) {
    7. float radian = DEG2RAD(i);
    8. int x = r * sin(radian) + pt1.x;
    9. int y = r * cos(radian) + pt1.y;
    10. GT::intV2 pt2(x, y);
    11. _canvas->drawLine(pt1, pt2, _color);
    12. }
    13. //在这里画到设备上,hMem相当于缓冲区
    14. BitBlt(hDC, 0, 0, wWidth, wHeight, hMem, 0, 0, SRCCOPY);
    15. }

    实现——画一圈彩色线:

    1. //=========画线算法Brensenhem===
    2. void drawLine(Point pt1,Point pt2);
    3. //=========线性插值Lerp=========
    4. inline RGBA colorLerp(RGBA _color1, RGBA _color2, float _scale) {
    5. RGBA _color;
    6. _color.m_r = _color.m_r + (float)(_color2.m_r - _color1.m_r) * _scale;
    7. _color.m_g = _color.m_g + (float)(_color2.m_g - _color1.m_g) * _scale;
    8. _color.m_b = _color.m_b + (float)(_color2.m_b - _color1.m_b) * _scale;
    9. _color.m_a = _color.m_a + (float)(_color2.m_a - _color1.m_a) * _scale;
    10. return _color;
    11. }
    1. #include "Canvas.h"
    2. #include
    3. #include "GTMATH.hpp"
    4. namespace GT {
    5. void GT::Canvas::drawLine(Point pt1, Point pt2) {
    6. int disX = abs(pt2.m_x - pt1.m_x);
    7. int disY = abs(pt2.m_y - pt1.m_y);
    8. int xNow = pt1.m_x;
    9. int yNow = pt1.m_y;
    10. int stepX = 0;
    11. int stepY = 0;
    12. //判断两个方向步进的正负
    13. stepX = pt1.m_x < pt2.m_x ? 1 : -1;
    14. stepY = pt1.m_y < pt2.m_y ? 1 : -1;
    15. //对比xy偏移量,决定步进方向选取x or y
    16. int sumStep = disX;
    17. bool useXStep = true;
    18. if (disX < disY) {
    19. sumStep = disY;
    20. useXStep = false;
    21. SWAP_INT(disX, disY);
    22. }
    23. //初始化P
    24. int p = 2 * disY - disX;
    25. for (int i = 0; i < sumStep; i++) {
    26. RGBA _color;
    27. float _scale = 0;
    28. if (useXStep) {
    29. _scale = (float)(xNow - pt1.m_x) / (float)(pt2.m_x - pt1.m_x);
    30. }
    31. else {
    32. _scale = (float)(yNow - pt1.m_y) / (float)(pt2.m_y - pt1.m_y);
    33. }
    34. _color = colorLerp(pt1.m_color, pt2.m_color, _scale);
    35. drawPoint(xNow, yNow, _color);
    36. if (p >= 0) {
    37. if (useXStep) {
    38. yNow += stepY;
    39. }
    40. else {
    41. xNow += stepX;
    42. }
    43. p = p - 2 * disX;
    44. }
    45. //步进主坐标
    46. if (useXStep) {
    47. xNow += stepX;
    48. }
    49. else {
    50. yNow += stepY;
    51. }
    52. p = p + 2 * disY;
    53. }
    54. }
    55. }
    1. void Render() {
    2. _canvas->clear();
    3. GT::Point pt1(100, 100, GT::RGBA(255, 0, 0));
    4. float r = 100;
    5. for (int i = 0; i < 360; i += 20) {
    6. float radian = DEG2RAD(i);
    7. int x = r * sin(radian) + pt1.m_x;
    8. int y = r * cos(radian) + pt1.m_y;
    9. GT::Point pt2(x, y, GT::RGBA(0, 255, 0));
    10. _canvas->drawLine(pt1, pt2);
    11. }
    12. //在这里画到设备上,hMem相当于缓冲区
    13. BitBlt(hDC, 0, 0, wWidth, wHeight, hMem, 0, 0, SRCCOPY);
    14. }

  • 相关阅读:
    沿SVG路径的颜色渐变
    单链表基本练习-删除
    k8s探针详解
    【数据结构】堆的创建
    热烈祝贺〖金融之路CapLab官方中文社群②群〗成员数突破1,600人!
    怎样将例化的uvn test包含在verdi的instance中,并将其中变量加入到dump的波形中(方便verdi追test以及debug)
    CSS box-shadow阴影
    如何成为一名云计算构架师,看这里!
    【MySQL】MySQL中出现错误代码: 1052 Column ‘xxx‘ in field list is ambiguous的原因和解决方法
    浅谈高并发分布式架构演进路径
  • 原文地址:https://blog.csdn.net/Jason6620/article/details/127835432