我想在640x480 VGA显示器上输出一个半径为100像素的移动红色圆圈。我一直纠结于如何制作和填充实际的圆圈。现在我看了一下令人麻木的Bresenham算法,但我无法使它们适合我的verilog代码。
我已经推测了我能做什么,但我不完全确定这是可能的。我知道圆的方程是(x-a)(x-a,y-b)(y-b)=r*r,其中(a,b)是原点。所以我想用RGB颜色填充这个圆圈,并将其从上向下移动,即在a=640/2 时从b=0移动到480
由于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问题,但可能是我在基础三角学方面失败了,但我真的对此感到困惑。请,如果我在尝试做这个循环时非常偏离(代码方面,逻辑方面,综合方面),一定要告诉我。如有任何帮助,我们将不胜感激。
我不确定嵌套的for循环会像你想象的那样表现,至少对于合成设计来说是这样。
我的建议是不要根据圆的参数方程来生成圆,我认为这是你想要做的。相反,如果圆总是相同的(即它的半径总是小于或等于100像素),请预先生成一个圆的位图,并将其作为一个大精灵存储在FPGA(块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
您不希望出现这样的循环。如果要动态生成圆(而不是使用位图),请在单独的过程中跟踪当前坐标(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个乘法器已经不算什么了(或者如果你以像素时钟的倍数操作,你可以以牺牲输入上的mux为代价共享乘法器)