Matlab程序——3d玫瑰

效果展示

经典玫瑰
紫色妖姬
其它作品

项目简介

  • 软件平台:Matlab;
  • 软件功能:
    • 画出一个,比较逼真的玫瑰花,形状可以调节;
    • 修改相关参数,理论上可以画出其他种类的花,以及形状类似的物品;
  • 灵感来源:奶油蛋糕的一种裱花手法;
  • 项目缘由:送给我爱人的情人节礼物~
  • 代码水平:新手水平,非专业向;
  • 备注说明:限于篇幅等原因,没有上传全部代码。包括一些新功能,更多可控参数,以及set函数,flower父类等等,不算核心内容;花萼见“局部算法-多花瓣”,花枝只是简单的弯曲圆柱,就不做上传了。
  • 完整项目:https://download.csdn.net/download/WoodenHouser/12657865

程序代码

基本思路

  • 核心原理——像素图像,空间点阵:
    • 求出double points[][][3],然后surf(points)画出。
  • 生成花瓣——基于一个圆柱侧面:
    在这里插入图片描述
    • 按起止角度纵向切割,横截面从整圆变为圆弧;
    • 母线由直线变为曲线,即r(h)由常数变为0-m-n-1(m>n)的变量;
    • 上侧边缘剪去两边直角,修成圆滑的曲线,并处理锯齿;
  • 生成花朵——多个花瓣组合:
    • 按照递增半径i,生成n个同心圆排列的花瓣;
    • 其中花瓣的各参数按i做了调节,这样更自然;
    • 生成花枝花萼等等。
main(){
	rose=new rose();
	rose.render();
}
class rose{
	render(){
		for(int i=0;i<this.petal_num;i++){
			petal=new petal();
			petal.render();
		}
	}
}
class petal{
	render(){
		points=get_points();
		surf(points);
	}
}

局部算法

抗锯齿算法:

% 核心原理:用三角形填充锯齿的缝隙,使边缘由锯齿状变为多边形状;
% 实现方式:将原本锯齿外应该删掉的点不要删掉,而是修改他们的坐标,将他们平移到锯齿边缘
for(int i=0;i<points.column_num;i++){
	pocessing_column=points[][i];
	longer_column=longer(points[][i-1],points[][i+1]);
	for(int j=pocessing_column.row_num;j<longer_column.row_num;j++){
		pocessing_column[j]=pocessing_column[row_num];
	}
}

母线曲线算法:

% 输入一系列点的坐标;
% 以这些点为拐点,用分段三角函数连接每两个点;
	% 如y=sin(x)连接了[-pi/2,-1],[pi/2,1]...这一系列点;
% 将每段放缩至y=0-1的范围,然后apply幂函数运算,y=y^a,调整曲线凹凸性,运算完了再放缩回原y范围。
% 备注:由于更换此函数即可改变花瓣形状,且此函数具有一定的通用性,所以做成外置独立函数。

对切割圆柱,母线弯曲的优化:

% 实际代码中自始至终都没有,先调用cylinder()函数生成完整的圆柱,再切割变弯。
% 而是首先生成母线和圆弧,之后直接利用矩阵叉乘生成曲面。

随机化算法:

% 将花瓣参数按花瓣序号做一定的处理,使其不完全一样,具有一定的波动,从而使花朵看上去更自然。
% 如花瓣的起始角度,大小,高度,等等。

图像描边算法:

% 刚开始画出来的图像效果不好,后来借用绘画中描边的思想,用较边缘颜色稍浅的颜色描一圈,效果更好。
% 该部分代码较为生硬,未优化,且逻辑上属于主过程之外的附属过程。

多类型花瓣算法:

% 由于花瓣的参数由花朵类负责生成,所以在花朵类中将函数“花瓣参数(花瓣序号)”改为分段函数,即可实现一朵花中有多种花瓣
% 可用于实现花萼,花蕊,复杂的花朵等等。

程序文档

class Rose{
	% 属性:
	Flower_Attribute;	% 花朵通用参数,构造时输入
	Rose_Attribute;		% 玫瑰特征参数,预先指定
	Petals_Attribute;	% 花瓣控制参数,构造时计算
	% 方法:
    Render(){}			% 在画板上画出本玫瑰
	Get_Petal(){}		% 返回第i个花瓣
	Get_Petal_Attribute(){}% 返回所有花瓣的对应属性
}
class Petal{
	% 属性:
	Petal_Attribute; 	% 花瓣参数,构造时输入
	% 方法:
    Render(){}			% 在画板上画出本花瓣
    Get_Matrix(){}		% 返回像素点阵的坐标
    Get_Color(){}		% 返回每个像素点颜色
	Get_Cylinder(){}	% 生成基础花瓣胚
	TrimFillet(){}		% 将花瓣修剪圆角
	ApplySize(){}		% 将花瓣拉伸大小
}

程序源码

"Draw_Rose.m"文件:

% 清空窗口
clf;
clear;
clc;
% 环境准备
axis equal;
hold on;
xlabel('x');
ylabel('y');
zlabel('z');
% 参数准备
fineness=1; % 渲染精细度
flower_position=[1,2,3]; % 花托位置
flower_size=1; % 放大倍数
petal_number=8; % 花瓣数量
% 画出玫瑰
rose=Rose(fineness,flower_position,flower_size,petal_number);
rose.Render();

"Rose.m"文件:

classdef Rose
    
    properties
         % 花朵控制参数
        fineness=1;% 渲染精细度
        flower_position=[0,0,0];% 花托位置
        flower_size=1;% 放大倍数
        petal_number=8;% 花瓣数量
        % 花瓣控制参数
        petal_size;% [size_x,size_y,size_z;...]
        petal_pixel;% [pixel_xy,pixel_z;...]
        petal_theta;% [theta_start,theta_range;...]
        petal_radius_z;% radius_z[][]
        petal_fillet;% fillet[][]
        petal_color;% [red,green,blue,dark;...]
        petal_line_c;% [red,green,blue;...]
    end
    
    properties(Hidden)
        petal_ratio=[1,1,1];% 花瓣大小比例
        petal_theta_range=270;% 花瓣角度大小
        petal_inflexion_point=[% 花瓣竖向曲线拐点
            0,0.32,0.6,1;
            0,1,0.85,1.15];
        petal_inflexion_power=[1/4;1;1];% 花瓣径向凹凸程度(凹nan——0凸)
    end
    
    methods
        % 构造函数
        function this=Rose(fineness,flower_position,flower_size,petal_number)
            % 参数录入
            this.fineness=fineness;
            this.flower_position=flower_position;
            this.flower_size=flower_size;
            this.petal_number=max(0,round(petal_number));
            % 参数计算
            this.petal_size=this.Get_Petal_Size();
            this.petal_pixel=this.Get_Petal_PixelNum();
            this.petal_theta=this.Get_Petal_Theta();
            this.petal_radius_z=this.Get_Petal_Radius_z();
            this.petal_fillet=this.Get_Petal_Fillet();
            this.petal_color=this.Get_Petal_Color();
            this.petal_line_c=this.Get_Petal_Line_C();
        end
        
        % 渲染图像
        function Render(this)
            % 渲染花朵
            for petal_sequence=1:this.petal_number
                % 生成并渲染花瓣
                petal_rose=this.Get_Petal(petal_sequence);
                petal_rose.Render();
            end
            % 关闭网格
            shading interp;
        end
        
        % 花瓣生成
        function petal_rose=Get_Petal(this,petal_sequence)
            % 数据准备
            size=this.petal_size(petal_sequence,:);
            pixel=this.petal_pixel(petal_sequence,:);
            theta=this.petal_theta(petal_sequence,:);
            radius_z=this.petal_radius_z(petal_sequence,:);
            fillet=this.petal_fillet(petal_sequence,:);
            color=this.petal_color(petal_sequence,:);
            line_c=this.petal_line_c(petal_sequence,:);
            % 花瓣生成
            petal_rose=Petal(size,pixel,theta,radius_z,fillet,color,line_c);
        end
        
        % 花瓣大小计算
        function petal_size=Get_Petal_Size(this)
            petal_size=zeros(this.petal_number,3);
            for petal_sequence=1:this.petal_number(1)
                size_x=this.petal_ratio(1)*this.flower_size*sin(petal_sequence/this.petal_number(1))/sin(1);
                size_y=this.petal_ratio(2)*this.flower_size*sin(petal_sequence/this.petal_number(1))/sin(1);
                A=0.15;
                size_z=this.flower_size*(1-A+A*(sin((petal_sequence/this.petal_number(1))*pi)+petal_sequence/this.petal_number(1)));
                % 集成输出
                petal_size(petal_sequence,:)=[size_x,size_y,size_z];
            end
        end
        
        % 花瓣像素数计算
        function petal_pixel=Get_Petal_PixelNum(this)
            petal_pixel=zeros(this.petal_number,2);
            for petal_sequence=1:this.petal_number(1)
                % 数据运算
                pixel_xy=16*(this.petal_theta_range/180)*this.fineness;
                A=0.2;
                pixel_xy=pixel_xy*(A+(1-A)*petal_sequence/this.petal_number(1))+1;
                pixel_z=16*this.petal_ratio(3)*this.fineness+1;
                % 数据规范
                pixel_xy=max(3,round(pixel_xy));
                pixel_z=max(2,round(pixel_z));
                % 集成输出
                petal_pixel(petal_sequence,:)=[pixel_xy,pixel_z];
            end
        end
        
        % 花瓣角度计算
        function petal_theta=Get_Petal_Theta(this)
            petal_theta=zeros(this.petal_number,2);
            theta_start_random=32;
            theta_range_random=32;
            for petal_sequence=1:this.petal_number(1)
                theta_start=150*petal_sequence+unifrnd(-theta_start_random,theta_start_random);
                theta_start=rem(theta_start,360);
                theta_range=this.petal_theta_range+unifrnd(-theta_range_random,theta_range_random);
                % 集成输出
                petal_theta(petal_sequence,:)=[theta_start,theta_range];
            end
        end
        
        % 花瓣半径计算
        function petal_radius_z=Get_Petal_Radius_z(this)
            pixel_z=max(this.petal_pixel(:,2));
            petal_radius_z=zeros(this.petal_number,pixel_z);
            points=this.petal_inflexion_point;
            num=size(points,2);
            start=points(1,1);
            ends=points(1,num);
            points(:,1)=2.*points(:,1)-points(:,2);
            points(:,num)=2.*points(:,num)-points(:,num-1);
            for petal_sequence=1:this.petal_number(1)
                pixel_z=this.petal_pixel(petal_sequence,2);
                r_z=Curve_cos_power(points,this.petal_inflexion_power,start,ends,pixel_z);
                % 集成输出
                petal_radius_z(petal_sequence,1:pixel_z)=r_z;
            end
        end
        
        % 花瓣圆角计算
        function petal_fillet=Get_Petal_Fillet(this)
            pixel_xy=max(this.petal_pixel(:,1));
            petal_fillet=zeros(this.petal_number,pixel_xy);
            for petal_sequence=1:this.petal_number(1)
                pixel_xy=this.petal_pixel(petal_sequence,1);
                xy=linspace(-1,1,pixel_xy+2);
                fillet_=1-xy.^4;
                fillet_=fillet_(2:pixel_xy+1);
                % 集成输出
                petal_fillet(petal_sequence,1:pixel_xy)=fillet_;
            end
        end
        
        % 花瓣颜色计算
        function petal_color=Get_Petal_Color(this)
            petal_color=zeros(this.petal_number,4);
            % color_=[0.35,0,0.5,0.36];
            color_=[0.64,0,0,0.36];
            for petal_sequence=1:this.petal_number(1)
                petal_color(petal_sequence,:)=color_;
            end
        end
        
        % 花瓣边色计算
        function petal_line_color=Get_Petal_Line_C(this)
            petal_line_color=zeros(this.petal_number,3);
            % color_=[245,215,0]./255;
            color_=[0.64,0,0];
            for petal_sequence=1:this.petal_number(1)
                % 集成输出
                petal_line_color(petal_sequence,:)=color_;
            end
        end
    end
end

"Petal.m"文件:

classdef Petal
    properties
        size;% 花瓣大小[size_x,size_y,size_z]
        pixel;% 像素点数[pixel_xy,pixel_z]
        theta;% 角度起止[theta_start,theta_end]
        radius_z;% 母线函数radius_z[]
        fillet;% 圆角函数fillet[]
        color;% 花瓣颜色[color_x,color_y,color_z]
        line_c;% 描边颜色[linec_x,linec_y,linec_z]
    end
    methods
        % 构造函数
        function this=Petal(size,pixel,theta,radius_z,fillet,color,line_c)
            this.size=size;
            this.pixel=pixel;
            this.theta=theta;
            this.radius_z=radius_z;
            this.fillet=fillet;
            this.color=color;
            this.line_c=line_c;
        end
        % 渲染图像
        function Render(this)
            [x,y,z]=this.Get_Matrix();
            c=this.Get_Color(z);
            surf(x,y,z,c);
            % 勾勒边缘
            m=this.pixel(2);
            n=this.pixel(1);
            lx=ones(1,n);
            ly=ones(1,n);
            lz=ones(1,n);
            for j=1:n
                i=2;
                while i<=m&&~isnan(z(i,j))
                    i=i+1;
                end
                lx(j)=x(i-1,j);
                ly(j)=y(i-1,j);
                lz(j)=z(i-1,j);
            end
            plot3(lx,ly,lz,'Color',this.line_c);
            % 描侧边
            % 备注:可优化。
            plot3(x(:,1),y(:,1),z(:,1),'Color',this.line_c);
            plot3(x(:,n),y(:,n),z(:,n),'Color',this.line_c);
        end
        % 矩阵生成
        function [x,y,z]=Get_Matrix(this)
            % 获取基本矩阵
            [x,y,z]=this.Get_Cylinder();
            % 剪裁圆角
            [x,y,z]=this.TrimFillet(x,y,z);
            % 调节比例
            [x,y,z]=this.ApplySize(x,y,z);
        end
        % 颜色生成
        function c=Get_Color(this,z)
            m=this.pixel(1);
            n=this.pixel(2);
            % 颜色矩阵
            color_0=ones(size(z,1),size(z,2));
            % 渐变优化
            for j=1:m
                % 边缘渐变
                hard_xy=0.35;
                hard_xy=hard_xy*this.color(4)*(((j-0.5*(1+m))^2)/((1-0.5*(1+m))^2));
                dark_hard=this.color(4)-hard_xy;
                for i=1:n
                    % 上下渐变
                    color_=z(i,j)/(this.fillet(j)*this.size(3));
                    % 暗度调整
                    color_=dark_hard*color_;
                    % 输出颜色
                    color_0(i,j)=1-color_;
                end
            end
            % RGB上色
            c(:,:,1)=this.color(1)*color_0;
            c(:,:,2)=this.color(2)*color_0;
            c(:,:,3)=this.color(3)*color_0;
        end
        % 基本柱面生成
        function [x,y,z]=Get_Cylinder(this)
            % 曲柱面生成
            r=this.radius_z(1:this.pixel(2));
            theta=linspace(this.theta(1),this.theta(2),this.pixel(1));
            x=r'*cos(theta);
            y=r'*sin(theta);
            z=(0:length(r)-1)'/(length(r)-1)*ones(1,this.pixel(1));
        end
        % 剪裁圆角
        function [x,y,z]=TrimFillet(this,x,y,z)
            m=this.pixel(2);
            n=this.pixel(1);
            % 精准点校准
            lambd=0;
            edge_z=0;
            for j=1:n
                edge_z=this.fillet(j);
                for k=1:m
                    if (z(k,j)==edge_z)
                        break;
                    elseif (z(k,j)>edge_z)
                        lambd=(edge_z-z(k-1,j))/(z(k,j)-z(k-1,j));
                        x(k,j)=x(k-1,j)+lambd*(x(k,j)-x(k-1,j));
                        y(k,j)=y(k-1,j)+lambd*(y(k,j)-y(k-1,j));
                        z(k,j)=edge_z;
                        break;
                    end
                end
            end
            % 逐列镂空
            for j=1:n
                edge_z=this.fillet(j);
                for k=1:m
                    if (z(k,j)>edge_z)
                        x(k,j)=nan;
                        y(k,j)=nan;
                        z(k,j)=nan;
                    end
                end
            end
            % 三角填充
            % 从左侧向中心
            left_begin=1;
            left_end=floor(n/2);
            for j=left_begin:1:left_end
                for k=1:m
                    if (isnan(z(k,j))&&~isnan(z(k,j+1)))
                        x(k,j)=x(k-1,j);
                        y(k,j)=y(k-1,j);
                        z(k,j)=z(k-1,j);
                    end
                end
            end
            % 从右侧向中心
            right_begin=n;
            right_end=left_end+1;
            for j=right_begin:-1:right_end
                for k=1:m
                    if (isnan(z(k,j))&&~isnan(z(k,j-1)))
                        x(k,j)=x(k-1,j);
                        y(k,j)=y(k-1,j);
                        z(k,j)=z(k-1,j);
                    end
                end
            end
        end
        % 调整大小
        function [x,y,z]=ApplySize(this,x,y,z)
            x=0.5*this.size(1)*x;
            y=0.5*this.size(2)*y;
            z=this.size(3)*z;
        end
    end
end

"Curve_cos_power.m"文件:

function line_y=Curve_cos_power(point,power,x_begin,x_end,pixel_num)
% 曲线生成函数
% 算法:
% 1.以三角函数连接所有点
% 2.以幂函数分段调整曲线
% IO:
% 1.输入多个坐标点
% 2.输出曲线列向量
% 3.输入参数的格式说明:
%     point=[% 极值点
%         x1,x2,...;
%         y1,y2,...]
%     power=[% 凹凸性
%         power12;
%         power23;...]
%     曲线定义域=
%         linspace(x_start,x_end,pixel_num)
% 4.输出参数的格式说明:
%     曲线值域:
%         line_y=zeros(1,pixel_num);
% 数据准备
line_x=linspace(x_begin,x_end,pixel_num);
line_y=zeros(1,pixel_num);
point_num=size(point,2);
point_x=point(1,:);
point_y=point(2,:);
% 针对逐点处理的时间优化
flag_start=1;
% 从左到右逐单调区间处理
for i=1:point_num-1
    % 计算三角函数参数
    omega=pi/(point_x(i+1)-point_x(i));
    phi=point_x(i)*pi/(point_x(i)-point_x(i+1));
    A=0.5*(point_y(i)-point_y(i+1));
    y0=point_y(i)-A;
    % 从左到右逐点处理
    for j=flag_start:pixel_num
        % 如果点在定义域内
        if point_x(i)<=line_x(j) && line_x(j)<=point_x(i+1)
            % apply_cos
            if mod(omega*line_x(j)+phi,pi)==pi/2
                % 零点精确值
                line_y(j)=y0;
            else
                line_y(j)=A*cos(omega*line_x(j)+phi)+y0;
            end
        end
        % 定义域外侧点舍弃
        if point_x(i+1)<=line_x(j)
            % 计算0-1放缩参数
            % y=(x-b)/a
            a=line_y(j-1)-line_y(flag_start);
            b=line_y(flag_start);
            for k=flag_start:j-1
                % apply_power
                line_y(k)=(line_y(k)-b)/a;% 放缩to0-1
                % 分正负处理
                if line_y(k)>=0
                    line_y(k)=line_y(k)^power(i);
                else % 避免虚数
                    line_y(k)=-(-line_y(k))^power(i);
                end
                line_y(k)=a*line_y(k)+b;% 放缩from0-1
            end
            flag_start=j;
            break;
        end
    end
end
end

更新日志

2020-07-22:修复了“Curve_cos_power”函数的已知bug,包括0-1放缩以及边界判定。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值