我想在640x480 VGA显示器上输出半径为100像素的移动红圈。我坚持如何制作并填写实际的圆圈。 现在我已经看过麻烦的Bresenham算法,但是我无法让它们适合我的verilog代码。
我已经理论化了我能够做到的事情,但我并不完全确定这是可能的。 我知道圆的方程是(x-a)(x-a)+(y-b)(y-b)= r * r其中(a,b)是原点。 所以我想用RGB颜色填充这个圆圈并从顶部到底部移动,即从b = 0到480,a = 640/2
由于VGA从左到右输出像素,向下迭代,我猜我必须使用嵌套循环。 我知道垂直移动的第一个循环从0到480。 这是我遇到问题的第二个循环。它从0到640但我希望像素在到达(x,y)(沿着圆的一个点)时改变颜色,保留该颜色,然后在它们通过后改变回来(x + k)(其中k是一个水平的和弦)。
基本上如果下面是我的圆圈的水平和弦:
_ 黑色 。(x,y) ___ 红色(k像素) _ 。(x + k,y) _ 黑色
reg [8:0] bally;//y coordinate of the center of circle
reg [8:0] rad;//radius
always @ (posedge clk)
begin
for(VCount=0;VCount<=480;VCount++)
for(HCount=0;HCount<=640;HCount++)
if((HCount*HCount>=rad*rad-((VCount-bally)*(VCount-bally)) && HCount*HCount<=((10000-((VCount-bally)*(VCount-bally)))+k)))
R<=1;
G<=0;
B<=0;
end
我的问题是,我如何根据我已经拥有的变量在我的if条件中表示k? 我意识到这可能不是一个verilog问题,但可能是我在基本三角测量法上失败但我的思想确实在此被阻止了。 并且,如果我在试图制作这个圈子时非常偏离(代码方面,逻辑方面,合成方式),请告诉我。 任何帮助将不胜感激。
答案 0 :(得分:3)
我不确定嵌套for循环的行为是否与您认为的一样,至少对于合成设计而言。
我的建议是不要根据其参数方程生成圆,这是我认为你想要做的。相反,如果圆总是相同的(即它的半径始终为100像素或更小),则预生成圆的位图并将其作为大型精灵存储在FPGA(Block RAM)内存中。它不会占用你太多的空间。精灵中的0位将翻译为“透明”,而1位则表示使用与精灵相关的颜色(圆圈)绘制此点。
这样,用于绘制屏幕的代码(实际上是屏幕的活动部分)将是这样的:
`define SPRLEN 256 // how many pixels in width and heigh the sprite will have.
`define SPRITECOLOR 3'b100; // RED ball. (RGB, 1-bit per channel)
`define BGCOLOR 3'010; // GREEN background
reg ballsprite[0:`SPRLEN*`SPRLEN-1]; // A matrix to hold your sprite.
reg [9:0] hcont, vcont; // horizontal and vertical counters for scanning a VGA picture
reg [8:0] ballx, bally; //coordinates of the left-top corner of the ball sprite
reg [7:0] coorx,coory; //X,Y coordinates of the current sprite position to draw
initial begin // read your sprite definition into memory. FPGA synthetizable.
$readmemb ("mysprite.bin", ballsprite);
end
// process to obtain relative coordinates of the current screen position within the sprite
always @* begin
coorx = hcont+1-ballx;
coory = vcont+1-bally;
end
// process to get the next dot from the sprite. We begin at ballx-1,bally-1 because
// there's one clock delay in getting the current sprite dot value.
always @(posedge clk) begin
if (hcont>=ballx-1 && hcont<=ballx-1+`SPRLEN && vcont>=bally-1 && vcont<=bally-1+`SPRLEN)
dot_from_ball <= ballsprite[{coory,coorx}]; // i.e. coory*256+coorx
else
dot_from_ball <= 1'b0; // if scan trace is not under the boundaries of the sprite,
// then it's transparent color
end
// multiplexer to actually put an RGB value on screen
always @* begin
if (hcont>=0 && hcont<640 && vcont>=0 && vcont<480) begin
if (dot_from_ball)
{R,G,B} = `SPRITECOLOR;
else
{R,G,B} = `BGCOLOR;
end
else
{R,G,B} = 3'b000; //if not into active area, mute RGB
end
您可以为hcont和vcont生成正确的值,并根据这些计数器何时达到某些值来推断同步信号。
前段时间,我做了类似的事情:将一个移动的精灵画入VGA屏幕。我是用Handel C做的,但逻辑是一样的。你可以在这里看到它: http://www.youtube.com/watch?v=wgDSzC-vGZ0
答案 1 :(得分:2)
你不想要那样的循环。如果要动态生成圆(而不是使用位图),请在单独的进程(always
块)中跟踪当前坐标。每个始终块将在VGA时钟的每个刻度线上运行,因此每次块执行时都会有一个新的像素位置。
然后有另一个always
块,它查看那些x和y坐标并确定当前点是否在圆圈中。根据该决定,您可以选择前景色或背景色。同样,这对每个像素按顺序运行一次。
棘手的部分是决定当前点是否在圈内。
如果这看起来有点难以开始,请绘制一个正方形,因为方程非常简单。一旦你了解它并确切地了解它是如何工作的,为什么它可以工作,你可以毕业到圆圈(以及其他参数化定义的形状)。要查找在运行时渲染这些形状的方法,在扫描图像时,您可能会发现术语“扫描线渲染”或“增量渲染”很有用。
在考虑更多这方面的问题时,不是圆形的标准方程式的情况:
x^2 + y^2 = r^2
可以渲染为不等式以返回圆圈内的点:
x^2 + y^2 < r^2
扩展以允许中心点的变化:
(x-xc)^2 + (y-yc)^2 < r^2
这意味着对于每个像素,您从x和y中减去中心点,将它们平方并与r^2
进行比较。如果它小于r ^ 2,则绘制像素。
这在老式FPGA中应该是很多逻辑,但是现在有3个减法器和2个乘法器(或者如果你以像素时钟的倍数运行,你可以分担乘数而牺牲关于输入的多路复用器)