-
Notifications
You must be signed in to change notification settings - Fork 0
/
local-search.xml
1340 lines (643 loc) · 330 KB
/
local-search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Prefect Forward 实现</title>
<link href="/2022/04/15/C++PrefectForward/"/>
<url>/2022/04/15/C++PrefectForward/</url>
<content type="html"><![CDATA[<h2 id="Prefect-Forward-实现"><a href="#Prefect-Forward-实现" class="headerlink" title="Prefect Forward 实现"></a>Prefect Forward 实现</h2><p> 涉及到模板偏特化、万能引用、引用折叠、模板类型推导</p><h3 id="1-Move-reference"><a href="#1-Move-reference" class="headerlink" title="1. Move reference"></a>1. Move reference</h3><p> 利用模板偏特化,去除引用,保证返回一个不带引用的类型</p><pre><code class="hljs c++"><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T><span class="hljs-keyword">struct</span> <span class="hljs-title class_">RemoveReference</span>{ <span class="hljs-keyword">using</span> type=T; <span class="hljs-built_in">RemoveReference</span>() { std::cout<<<span class="hljs-string">"T"</span><<std::endl; }};<span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T><span class="hljs-keyword">struct</span> <span class="hljs-title class_">RemoveReference</span><T&>{ <span class="hljs-keyword">using</span> type=T; <span class="hljs-built_in">RemoveReference</span>() { std::cout<<<span class="hljs-string">"T&"</span><<std::endl; }};<span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T><span class="hljs-keyword">struct</span> <span class="hljs-title class_">RemoveReference</span><T&&>{ <span class="hljs-keyword">using</span> type=T; <span class="hljs-built_in">RemoveReference</span>() { std::cout<<<span class="hljs-string">"T&&"</span><<std::endl; }};</code></pre><h3 id="2-Universal-reference"><a href="#2-Universal-reference" class="headerlink" title="2. Universal reference"></a>2. Universal reference</h3><p> <code>&</code>是左值引用,但<code>&&</code>一定是右值引用吗,答案是否定的!</p><pre><code class="hljs c++">T&& t</code></pre><p>出现以上声明时,<strong>如果<code>T</code>是需要被推导出来的,比如模板,<code>auto</code>,那么<code>t</code>就不一定是右值引用了!</strong>其判断方法为引用折叠的规则</p><h3 id="3-Reference-Collapse"><a href="#3-Reference-Collapse" class="headerlink" title="3. Reference Collapse"></a>3. Reference Collapse</h3><p> 声明引用的引用编译器不会通过,但可以间接声明,比如模板类型<code>T</code>本身可以隐含引用&,再加上函数参数里的引用&</p><p> 引用折叠发生的情况有template、auto、typedef、decltype</p><pre><code class="hljs c++">x& & -- x&<span class="hljs-comment">//左值引用的左值引用等价于左值引用</span>x& && -- x&<span class="hljs-comment">//左值引用的右值引用等价于左值引用</span>x&& & -- x&<span class="hljs-comment">//右值引用的左值引用等价于左值引用</span>x&& && -- x&&<span class="hljs-comment">//右值引用的右值引用等价于右值引用</span></code></pre><p>总之任一个为左值引用,最终都是左值引用,反之为右值引用。</p><p> 例子:</p><pre><code class="hljs c++"><span class="hljs-type">int</span> a=<span class="hljs-number">10</span>;<span class="hljs-comment">//a 左值</span><span class="hljs-type">int</span> &b=a;<span class="hljs-comment">//b 左值引用</span><span class="hljs-keyword">auto</span>&& c=b;<span class="hljs-comment">//然鹅,c是左值引用;</span><span class="hljs-comment">//auto被推导为int(reference-ness),而又因为b是左值,故被推导为int&,最终形式为int& &&,根据引用折叠,即c为左值引用</span><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">fun1</span><span class="hljs-params">(<span class="hljs-type">int</span>&&t)</span></span>{...}<span class="hljs-comment">//t一定为右值引用</span><span class="hljs-function"><span class="hljs-keyword">template</span><<span class="hljs-keyword">class</span> T></span><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">fun2</span><span class="hljs-params">(T&&t)</span></span>{...}<span class="hljs-comment">//T被推导为何类型是不定的,因此t不一定为右值引用</span></code></pre><h3 id="4-Template-type-deduction"><a href="#4-Template-type-deduction" class="headerlink" title="4 .Template type deduction"></a>4 .Template type deduction</h3><ol><li><p>模板类型推导时,忽略引用类型参数的引用性(reference-ness)</p></li><li><p>给universal reference参数进行类型推导时,左值要特别对待(推导为<code>Type&</code>)</p></li><li><p>传值参数的类型推导,入参的诸如所有const /volatile的特性都会忽略</p></li><li><p>模板板类型推导过程中,数组或函数做微参数时会退化成指针,除非模板函数的参数是引用类型</p></li></ol><h3 id="5-move"><a href="#5-move" class="headerlink" title="5. move"></a>5. move</h3><p>move的实现主要有两点:</p><ol><li>接收万能引用,保证任何值都能传递</li><li>去除引用,然后强制转换成右值引用</li></ol><pre><code class="hljs c++"><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T><span class="hljs-keyword">typename</span> RemoveReference<T>::<span class="hljs-function">type && <span class="hljs-title">myMove</span><span class="hljs-params">(T&& val)</span></span><span class="hljs-function"></span>{ <span class="hljs-keyword">return</span> <span class="hljs-keyword">static_cast</span><<span class="hljs-keyword">typename</span> RemoveReference<T>::type&&>(val);}</code></pre><h3 id="6-forward"><a href="#6-forward" class="headerlink" title="6. forward"></a>6. forward</h3><p> 注意以下<code>foo</code>函数,如果传入的是左值,应该去调用<strong>左值引用</strong>类型的<code>check</code>函数,否则应该调用<strong>右值引用</strong>类型的<code>check</code>函数,这样便是完美转发</p><pre><code class="hljs c++"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">check</span><span class="hljs-params">(<span class="hljs-type">int</span> & val)</span></span><span class="hljs-function"></span>{ std::cout<<<span class="hljs-string">"this is lval"</span><<std::endl;}<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">check</span><span class="hljs-params">(<span class="hljs-type">int</span> && val)</span></span><span class="hljs-function"></span>{ std::cout<<<span class="hljs-string">"this is rval"</span><<std::endl;}<span class="hljs-function"><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T></span><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">foo</span><span class="hljs-params">(T&& val)</span></span><span class="hljs-function"></span>{ <span class="hljs-comment">//val一定是左值</span> std::cout<<<span class="hljs-string">"after forward:\n"</span>; <span class="hljs-comment">//虽然val一定是左值,但T保存着val原本究竟是左值还是右值的信息,利用它实现完美转发</span> <span class="hljs-built_in">check</span>(<span class="hljs-built_in">myForward</span><T>(val));}...<span class="hljs-built_in">foo</span>(<span class="hljs-number">1</span>);<span class="hljs-comment">//rval</span><span class="hljs-built_in">foo</span>(<span class="hljs-built_in">myMove</span>(x));<span class="hljs-comment">//rval</span><span class="hljs-built_in">foo</span>(x);<span class="hljs-comment">//lval</span></code></pre><p>完美转发的实现依靠以下两点:</p><p> 1.注意到<code>foo</code>函数中的val本身一定是一个<strong>左值</strong>!它在完美转发中只起到传值的作用!</p><p> 2.<code>val</code>原本的类型信息:左值还是右值?它被保存在<code>T</code>中,需要利用它</p><pre><code class="hljs c++"><span class="hljs-comment">//左值完美转发</span><span class="hljs-function"><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T></span><span class="hljs-function">T&& <span class="hljs-title">myForward</span><span class="hljs-params">(<span class="hljs-keyword">typename</span> RemoveReference<T>::type& lval)</span></span><span class="hljs-function"></span>{ <span class="hljs-keyword">return</span> <span class="hljs-built_in">static_cast</span><T&&>(lval);}<span class="hljs-comment">//右值完美转发,注意到右值转发成一个左值是错误的</span><span class="hljs-function"><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T></span><span class="hljs-function">T&& <span class="hljs-title">myForward</span><span class="hljs-params">(<span class="hljs-keyword">typename</span> RemoveReference<T>::type&& rval)</span></span><span class="hljs-function"></span>{ <span class="hljs-built_in">static_assert</span>(!std::is_lvalue_reference<T>::value,<span class="hljs-string">"rval forward to lval!"</span>); <span class="hljs-keyword">return</span> <span class="hljs-built_in">static_cast</span><T&&>(rval);}</code></pre><p> 以左值的完美转发为例,传入类型为自定义类型<code>Type</code>:</p><ol><li><p>如果外部函数<code>foo</code>传入的是一个右值,其形参<code>T&&</code>推导为<code>Type&&</code></p><p>由于<code>T</code>被推导为<code>Type</code>,因此调用<code>forward<Type></code>后传出右值引用</p></li><li><p>如果外部函数<code>foo</code>传入的是一个左值,则推导为<code>Type& &&</code>,根据引用折叠原则,其形参<code>T&&</code>最终推导为<code>Type &</code></p><p>由于<code>T</code>被推导成<code>Type&</code>,因此调用<code>forward<Type&></code>后传出左值引用</p></li></ol>]]></content>
<categories>
<category>C++</category>
</categories>
<tags>
<tag>Prefect Forward</tag>
</tags>
</entry>
<entry>
<title>C++ PCH提升编译速度</title>
<link href="/2021/09/30/C++PCH%E6%8F%90%E5%8D%87%E7%BC%96%E8%AF%91%E9%80%9F%E5%BA%A6/"/>
<url>/2021/09/30/C++PCH%E6%8F%90%E5%8D%87%E7%BC%96%E8%AF%91%E9%80%9F%E5%BA%A6/</url>
<content type="html"><![CDATA[<h3 id="C-PCH"><a href="#C-PCH" class="headerlink" title="C++ PCH"></a>C++ PCH</h3><h4 id="流程"><a href="#流程" class="headerlink" title="流程"></a>流程</h4><ol><li><p>创建<code>pch.h</code>,<code>pch.cpp</code></p><pre><code class="hljs c++"><span class="hljs-comment">//pch.h</span><span class="hljs-meta">#<span class="hljs-keyword">ifndef</span> __PCH_H__</span><span class="hljs-meta">#<span class="hljs-keyword">define</span> __PCH_H__</span><span class="hljs-comment">//include your header file (not likely to change)</span><span class="hljs-comment">//...</span><span class="hljs-comment">//...</span><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><span class="hljs-comment">//pch.cpp</span><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"pch.h"</span></span></code></pre></li><li><p>在vs项目属性中<code>C/C++</code>的预编译头中设置使用预编译头</p><img src="/2021/09/30/C++PCH%E6%8F%90%E5%8D%87%E7%BC%96%E8%AF%91%E9%80%9F%E5%BA%A6/image-20210930130804619.png" class=""></li><li><p>在【解决方案资源管理器】中右键选中<code>pch.cpp</code>文件属性设置创建预编译头</p><img src="/2021/09/30/C++PCH%E6%8F%90%E5%8D%87%E7%BC%96%E8%AF%91%E9%80%9F%E5%BA%A6/image-20210930131307993.png" class=""></li><li><p>在所有源文件中第一个包含该预编译头文件<code>pch.cpp</code>。重复地向所有源文件添加预编译头会很繁琐,可在项目的属性设置中的<code>C/C++</code>下的<code>高级</code>中设置强制包含文件</p><img src="/2021/09/30/C++PCH%E6%8F%90%E5%8D%87%E7%BC%96%E8%AF%91%E9%80%9F%E5%BA%A6/image-20210930131857997.png" class=""></li></ol><p><strong>ps</strong>:C++的预编译头是不能用在C上的,反之亦然。也就是说,假如预编译头是通过.cpp源文件生成的,那么在.c源文件中使用了这个预编译头就会导致编译出错,简单的做法是把源文件的扩展名改成.cpp,统一使用C++即可。</p><p>如果一些三方库的源代码文件不需要加入预编译头,如下解决:</p><ol><li>在解决方案资源管理器中,右击相应的.cpp文件,点击“属性”</li><li>在左侧配置属性中,点开“C/C++”,单击“预编译头”</li><li>更改右侧第一行的“创建/使用预编译头”,把选项从“使用预编译头(/Yu)”改成“不使用预编译头”</li><li>注:每一个报错的.cpp都要如此更改</li></ol>]]></content>
<categories>
<category>C++</category>
</categories>
<tags>
<tag>编译</tag>
</tags>
</entry>
<entry>
<title>Singularity-Constrained Octahedral Fields for Hexahedral Meshing</title>
<link href="/2021/03/17/2020-03-09-Singularity-Constrained-Octahedral-Fields-for-Hexahedral-Meshing/"/>
<url>/2021/03/17/2020-03-09-Singularity-Constrained-Octahedral-Fields-for-Hexahedral-Meshing/</url>
<content type="html"><![CDATA[<h3 id="1-概括"><a href="#1-概括" class="headerlink" title="1.概括"></a>1.概括</h3><p>  枚举了所有六面体网格中关于边的度的局部属性;推广了Hopf-Poincare index formula至octahedral fields;设计了一个通过求解非线性整形优化从一个预输入的奇异结构图生成一个优化后的octahedral field的算法。</p><h3 id="2-拓扑关系"><a href="#2-拓扑关系" class="headerlink" title="2.拓扑关系"></a>2.拓扑关系</h3><h4 id="2-1-Local-Topology-of-Eages"><a href="#2-1-Local-Topology-of-Eages" class="headerlink" title="2.1 Local Topology of Eages"></a>2.1 Local Topology of Eages</h4><center><img src="/2021/03/17/2020-03-09-Singularity-Constrained-Octahedral-Fields-for-Hexahedral-Meshing/image-20210312160618118.png" class=""></center>$$\operatorname{idx}(e)=\left\{\begin{array}{ll}\left(4-\operatorname{val}_{h}(e)\right) / 4 & \text { for interior } e \\ \left(2-\operatorname{val}_{h}(e)\right) / 4 & \text { for boundary } e\end{array}\right.$$对于内部边,我们希望的是$$I_{interior}=\{\frac{1}{4},0,-\frac{1}{4}\}$$,即边的度为3、4、5;对于外部边,则为$$I_{boundary}=\{\frac{1}{4},0,-\frac{1}{4},-\frac{1}{2}\}$$,即度为1、2、3、4,而对于其他的index,则可以通过插入模板来修改,如下<center><img src="/2021/03/17/2020-03-09-Singularity-Constrained-Octahedral-Fields-for-Hexahedral-Meshing/image-20210312161155180.png" class="" title="image-20210312161155180"></center><h4 id="2-2-Local-Topology-of-Vertex"><a href="#2-2-Local-Topology-of-Vertex" class="headerlink" title="2.2 Local Topology of Vertex"></a>2.2 Local Topology of Vertex</h4><p>  在CubeCover的论文中,有提到关于六面体网格中点拓扑的关系式,其用一个球包围该顶点,则连接该点的边在球上相交于一些点,连接这些点,形成三角形,则每个三角形对应一个六面体,如下</p><center><img src="/2021/03/17/2020-03-09-Singularity-Constrained-Octahedral-Fields-for-Hexahedral-Meshing/image-20210315190333661.png" class="" title="image-20210315190333661"></center><p>则根据欧拉公式<script type="math/tex">V-E+F=2</script>,以及面和边的关系<script type="math/tex">3F=2E</script>,以及点的度和边的关系<script type="math/tex">\sum_{v\in V}deg (v)=2E</script>,</p><p>则有如下关系</p><script type="math/tex; mode=display">\sum_{v \in V }(6-deg(v))=12</script><p>以上关系式中,顶点的度其实对应的是六面体网格中边的度,同时可以观察出,边的度若为6,其不做贡献。若设<script type="math/tex">i,j,k</script>分别为球上度为<script type="math/tex">3、4、5</script>的点的数量,则有<script type="math/tex">3i+2j+k=12</script>,又因为<script type="math/tex">i+j+k=V</script>,则可穷举出如下的11个解</p><center><img src="/2021/03/17/2020-03-09-Singularity-Constrained-Octahedral-Fields-for-Hexahedral-Meshing/image-20210315191329857.png" class="" title="image-20210315191329857"><img src="/2021/03/17/2020-03-09-Singularity-Constrained-Octahedral-Fields-for-Hexahedral-Meshing/image-20210315203959284.png" class="" title="image-20210315203959284"></center><p>上图中为11中情况的10种,剩下的未标出1种是标准的立方体块的情况<script type="math/tex">(0,6,0)</script>。</p><h4 id="2-3-全局条件"><a href="#2-3-全局条件" class="headerlink" title="2.3 全局条件"></a>2.3 全局条件</h4><script type="math/tex; mode=display">\sum_{v \in \partial V_{S}} \frac{1}{2}\left(1-\frac{\operatorname{val}_{h}(v)}{4}\right)-\sum_{e \in \partial E_{S}^{-}} \mathrm{idx}(e)+\sum_{v \in{V}^{'}_{S}}\left(1-\frac{\operatorname{val}_{h}(v)}{8}\right)-\sum_{e \in{E}_{S}^{'-}} \operatorname{idx}(e)=0</script><p>其中<script type="math/tex">\partial V_s,\partial V^{'}_{s}</script>分别为边界和内部的奇异图上的点,<script type="math/tex">\partial E^{-}_s,\partial E^{’-}_s</script>分别为不封闭的边界和不封闭的内部边。</p><h3 id="3-Integer-Grid-Maps(IGM)"><a href="#3-Integer-Grid-Maps(IGM)" class="headerlink" title="3.Integer-Grid Maps(IGM)"></a>3.Integer-Grid Maps(IGM)</h3><p>  通常,标架场生成六面体网格的思路是将其映射到一个参数域</p><center><img src="/2021/03/17/2020-03-09-Singularity-Constrained-Octahedral-Fields-for-Hexahedral-Meshing/image-20210317140800265.png" class="" title="image-20210317140800265"></center><p>这里新的一种观点是,一个IGM可以诱导出一个field,但是相反的,不是任一个field可以转换成一个IGM,如果这个field可以,则说明这个field是globally hex-meshable。</p><h3 id="4-Singularity-Graph-to-Octahedral-Field"><a href="#4-Singularity-Graph-to-Octahedral-Field" class="headerlink" title="4.Singularity Graph to Octahedral Field"></a>4.Singularity Graph to Octahedral Field</h3><h4 id="4-1-Singularity-Graph-Constraints"><a href="#4-1-Singularity-Graph-Constraints" class="headerlink" title="4.1 Singularity Graph Constraints"></a>4.1 Singularity Graph Constraints</h4><h5 id="4-1-1-Boundary-alignment"><a href="#4-1-1-Boundary-alignment" class="headerlink" title="4.1.1 Boundary alignment"></a>4.1.1 Boundary alignment</h5><p>  边界法向对齐标架</p><h5 id="4-1-2-Singular-arc-alignment"><a href="#4-1-2-Singular-arc-alignment" class="headerlink" title="4.1.2 Singular arc alignment"></a>4.1.2 Singular arc alignment</h5><p>  奇异边周围的标架之一与边相切</p><h5 id="4-1-3-Edge-type"><a href="#4-1-3-Edge-type" class="headerlink" title="4.1.3 Edge type"></a>4.1.3 Edge type</h5><p>  edge的one-ring cells 中的标架的matching matrix 的乘积</p><h5 id="4-1-4-Vertex-type"><a href="#4-1-4-Vertex-type" class="headerlink" title="4.1.4 Vertex type"></a>4.1.4 Vertex type</h5><ul><li>Tangent continuity </li><li>Corner constraint </li><li>Boundary sector constraint </li></ul>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>标架场</tag>
</tags>
</entry>
<entry>
<title>Algebraic Representations for Volumetric Frame Fields(一)</title>
<link href="/2020/10/30/2020-10-30-Algebraic-Representations-for-Volumetric-Frame-Fields(%E4%B8%80)/"/>
<url>/2020/10/30/2020-10-30-Algebraic-Representations-for-Volumetric-Frame-Fields(%E4%B8%80)/</url>
<content type="html"><![CDATA[<h4 id="1-解空间"><a href="#1-解空间" class="headerlink" title="1.解空间"></a>1.解空间</h4><p>  定义映射<script type="math/tex">\rho:SO(3)\to SO(9)</script>,定义线性算子<script type="math/tex">H \in \mathbb{R}^{9\times 9}</script>投影至<script type="math/tex">\mathbb{R}^9</script>的子空间(即在八面体群作用下具有不变性的空间)</p><script type="math/tex; mode=display">H=\frac{1}{|O|}\sum_{o \in O}\rho(o)</script><p>由于</p><script type="math/tex; mode=display">\rho\left(o^{\prime}\right) H=\frac{1}{|\mathrm{O}|} \sum_{o \in \mathrm{O}} \rho\left(o^{\prime}\right) \rho(o)=\frac{1}{|\mathrm{O}|} \sum_{o \in \mathrm{O}} \rho\left(o^{\prime} o\right)=\frac{1}{|\mathrm{O}|} \sum_{o \in \mathrm{O}} \rho(o)=H</script><p>所以<script type="math/tex">\forall q \in \mathbb{R}^9</script>,当且仅当<script type="math/tex">q\in Im(H)</script>时,<script type="math/tex">\rho(O)\cdot q=q</script>。</p><p>  说明当映射<script type="math/tex">\rho</script>作用于八面体群时并不改变算子<script type="math/tex">H</script>的作用,根据以上意义,可得出当<script type="math/tex">q\in Im(H)</script>时,<script type="math/tex">q</script>的稳定子<script type="math/tex">stab(q)=O</script></p><p>  定义规范八面体标架,所有其他标架可由其旋转变换得到</p><script type="math/tex; mode=display">q_{0}=\left(0,0,0,0, \sqrt{\frac{7}{12}}, 0,0,0, \sqrt{\frac{5}{12}}\right)^{\top} \in \mathbb{R}^{9}</script><p>则<script type="math/tex">H=q_0q_0^{\top}</script>。</p><p>  根据轨道与稳定子的关系,可得出解空间位于<script type="math/tex">q</script>的轨道<script type="math/tex">F</script>上,其稳定子为<script type="math/tex">O</script>,将其设为八面体变量(octahedral variety)</p><script type="math/tex; mode=display">F=\rho(\mathrm{SO}(3)) q_{0}=\left\{\rho(r) q_{0} \mid r \in \mathrm{SO}(3)\right\}</script><p>根据轨道-稳定子的理论,存在一个<script type="math/tex">\mathcal{F}\to F</script>的等变微分同胚映射<script type="math/tex">\phi</script>:</p><center><img src="/2020/10/30/2020-10-30-Algebraic-Representations-for-Volumetric-Frame-Fields(%E4%B8%80)/image-20201030133609350.png" class="" title="image-20201030133609350"></center><p>其中<script type="math/tex">\mathcal{F}=SO(3)/O</script>,它是一个spherical 3- manifold。</p><h4 id="2-度量"><a href="#2-度量" class="headerlink" title="2.度量"></a>2.度量</h4><p>  令<script type="math/tex">\alpha=\sqrt{\frac{3}{20}},F_\alpha=\alpha F=\{\alpha q:q\in F\}</script>,定义映射<script type="math/tex">\pi_a:\mathbb{R}^{9 \times 9}\to\mathbb{R}^9</script>表示矩阵乘以<script type="math/tex">\alpha</script>比例规范标架<script type="math/tex">q_a=\alpha q_0</script>,即<script type="math/tex">\pi_\alpha(A)=Aq_\alpha</script>。</p><p>  由于<strong><script type="math/tex">\pi_\alpha \circ \rho:SO(3)\to F_\alpha</script>是局部等距映射的,根据之前的交换图的结论,故<script type="math/tex">\mathcal{F}\to F_\alpha</script>也是局部等距映射。</strong></p><p>  <strong>证明:</strong></p><p>  用李代数定义<script type="math/tex">\rho</script>在单位阵处的微分</p><script type="math/tex; mode=display">(D\rho)_{I}: \mathfrak{s o}(3)=T_{I} \mathrm{SO}(3) \rightarrow \mathfrak{s o}(9) \subset \mathbb{R}^{9 \times 9}</script><p> <script type="math/tex">(D \rho)_{I}</script>具体为<script type="math/tex">L_i:=(D \rho)_{I}(l_i)</script>,即李代生成元的像,<script type="math/tex">\pi_\alpha</script>是一个线性映射,所以它的微分只要乘以<script type="math/tex">q_\alpha</script>就可以。</p><p>  对每个<script type="math/tex">g\in SO(3)</script>,李代数<script type="math/tex">T_g SO(3)</script>的正交基由李代数生成元的右平移给出</p><script type="math/tex; mode=display">\{(D R_g)_{I}l_i\}_{i=1}^{3}</script><p>因此可考虑映射<script type="math/tex">\pi_\alpha \circ \rho</script>的像在<script type="math/tex">T_{\rho(g)q_\alpha}F</script>下的正交基</p><script type="math/tex; mode=display">\begin{aligned}D\left(\pi_{\alpha} \circ \rho\right)_{g}\left(D R_{g}\right)_{I} l_{i} &=\left(D \pi_{\alpha}\right)(D \rho)_{g}\left(D R_{g}\right)_{I} l_{i} \\&=\left(D \pi_{\alpha}\right)\left(D R_{\rho(g)}\right)_{I}(D \rho)_{I} l_{i} \\&=\left(D \pi_{\alpha}\right) L_{i} \rho(g) \\&=L_{i} \rho(g) q_{\alpha}\end{aligned}</script><p>之后需要验证的是这组对象是否符合以下度量的形式?</p><script type="math/tex; mode=display"><L_iq,L_jq>=\delta _{ij},\forall q\in F_{\alpha}</script><p>  由于<script type="math/tex">\rho(g)q_\alpha \in F_\alpha</script></p><script type="math/tex; mode=display">\begin{aligned}\left\langle L_{i} \rho(g) q_{\alpha}, L_{j} \rho(g) q_{\alpha}\right\rangle &=\left\langle\rho(g)^{\top} L_{i} \rho(g) q_{\alpha}, \rho(g)^{\top} L_{j} \rho(g) q_{\alpha}\right\rangle \\&=\left\langle D \rho_{I}\left(g^{\top} l_{i} g\right) q_{\alpha}, D \rho_{I}\left(g^{\top} l_{j} g\right) q_{\alpha}\right\rangle\end{aligned}</script><p>其中第一个等号是因为<script type="math/tex">\rho(g) \in SO(9)</script>,具有旋转不变性,第二个等号是可由附录中的一个引理给出:<script type="math/tex">(D\rho)_I(g^{-1}ag)=\rho(g)^{-1}((D\rho)_Ia)\rho(g)</script>。</p><p>  若令<script type="math/tex">a_{ik}=a_{ik}(g)</script>表示<script type="math/tex">SO(3)</script>的伴随表示的系数,即<script type="math/tex">g^{\top}l_ig=\sum_k a_{ik}l_k</script>,这些系数组成了一个正交阵。根据这个定义可以线性化<script type="math/tex">D\rho</script>,则可得到以下</p><script type="math/tex; mode=display">\begin{aligned}\left\langle L_{i} \rho(g) q_{\alpha}, L_{j} \rho(g) q_{\alpha}\right\rangle&=\left\langle D \rho_{I}\left(g^{\top} l_{i} g\right) q_{\alpha}, D \rho_{I}\left(g^{\top} l_{j} g\right) q_{\alpha}\right\rangle\\&=\left\langle\sum_{r} a_{i r} L_{r} q_{\alpha}, \sum_{s} a_{j s} L_{s} q_{\alpha}\right\rangle \\&=\sum_{r, s} a_{i r} a_{j s}\left\langle L_{r} q_{\alpha}, L_{s} q_{\alpha}\right\rangle \\&=\sum_{k} a_{i k} a_{j k}=\delta_{i j}\end{aligned}</script><p><strong>得证。</strong></p><p>  因此,<script type="math/tex">F</script>是浸入在<script type="math/tex">\mathbb{R}^9</script>中的子流形,且等距同构于<script type="math/tex">\mathcal{F}=SO(3)/O</script>。因此,我们可在该浸入的流形上引入流形优化中的方法。</p>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>标架场</tag>
</tags>
</entry>
<entry>
<title>Green's Identities And Boundary Element Method</title>
<link href="/2020/10/09/2020-10-09-Greens-identities/"/>
<url>/2020/10/09/2020-10-09-Greens-identities/</url>
<content type="html"><![CDATA[<h3 id="1-高维分部积分"><a href="#1-高维分部积分" class="headerlink" title="1.高维分部积分"></a>1.高维分部积分</h3><p>  回忆下最基本的分部积分</p><script type="math/tex; mode=display">\begin{array}{c}\int u(x) v^{\prime}(x) d x=u(x) v(x)-\int u^{\prime}(x) v(x) d x \\\int u d v=u v-\int v d u\end{array}</script><p>  对于高维,无非是多了基底,对每个基的方向使用分部积分</p><script type="math/tex; mode=display">\begin{aligned}\int_{\Omega}\frac{\partial u}{\partial x_i}v_id\Omega&=\int_{\Omega}v_idu\\&= \int_\Gamma uv_in_id\Gamma-\int_{\Omega}udv_i\\&=\int_\Gamma uv_in_id\Gamma-\int_{\Omega}u\frac{\partial v_i}{x_i}d\Omega\\\end{aligned}</script><p>对<script type="math/tex">i</script>求和,写成散度形式,</p><script type="math/tex; mode=display">\int_{\Omega}\nabla u\cdot vd\Omega=\int_\Gamma uv\cdot d\Gamma-\int_{\Omega}u\nabla \cdot vd\Omega\\</script><h3 id="2-格林第一恒等式(Green’s-First-Identities)"><a href="#2-格林第一恒等式(Green’s-First-Identities)" class="headerlink" title="2.格林第一恒等式(Green’s First Identities)"></a>2.格林第一恒等式(Green’s First Identities)</h3><p>  把<script type="math/tex">v</script>用<script type="math/tex">\nabla v</script>代,即用梯度场代替,则变为</p><script type="math/tex; mode=display">\begin{aligned}\int_{\Omega}\nabla u\cdot \nabla vd\Omega&=\int_\Gamma u\nabla v\cdot d\Gamma-\int_{\Omega}u\nabla \cdot \nabla vd\Omega\\&=\int_\Gamma u\nabla v\cdot d\Gamma-\int_{\Omega}u\Delta vd\Omega\end{aligned}</script><p>或写成</p><script type="math/tex; mode=display">\int_{\Omega}(\nabla u\cdot \nabla v+u\Delta v)d\Omega=\int_\Gamma u\nabla v\cdot d\Gamma=\int_{\Gamma}u\frac{\partial v}{\partial n}d\Gamma</script><p>上式第二个等号用了方向导数的定义<script type="math/tex">\nabla v\cdot n=\frac{\partial v}{\partial n}</script>。</p><p>注:若令分部积分中<script type="math/tex">u=1</script>,则变为散度定理。</p><h3 id="3-格林第二恒等式(Green’s-Second-Identities)"><a href="#3-格林第二恒等式(Green’s-Second-Identities)" class="headerlink" title="3. 格林第二恒等式(Green’s Second Identities)"></a>3. 格林第二恒等式(Green’s Second Identities)</h3><p>  根据格林第一恒等式,推导格林第二恒等式。交换格林第一恒等式中的<script type="math/tex">u,v</script>有,</p><script type="math/tex; mode=display">\int_{\Omega}\nabla u\cdot \nabla vd\Omega=\int_\Gamma v\nabla u\cdot d\Gamma-\int_{\Omega}v\Delta ud\Omega</script><p>再根第一恒等式相减,得到第二恒等式</p><script type="math/tex; mode=display">\int_{\Gamma}(u\nabla v-v\nabla u)\cdot d\Gamma=\int_{\Gamma}(u\frac{\partial v}{\partial n}-v\frac{\partial u}{\partial n}) d\Gamma=\int_{\Omega}(u\Delta v-v\Delta u)d\Omega</script><h3 id="4-格林第三恒等式(Green’s-Third-Identities)"><a href="#4-格林第三恒等式(Green’s-Third-Identities)" class="headerlink" title="4. 格林第三恒等式(Green’s Third Identities)"></a>4. 格林第三恒等式(Green’s Third Identities)</h3><p>  设函数 <em>G</em> 是拉普拉斯方程的基本解,其满足</p><script type="math/tex; mode=display">\Delta G(P,Q)=\delta(P-Q)</script><p>其中<script type="math/tex">G</script>是格林函数,<script type="math/tex">\delta</script>是狄拉克<script type="math/tex">\delta</script>函数。</p><p>  若<script type="math/tex">P,Q\in \Omega,P^{\prime},Q^{\prime} \in \partial \Omega</script>,将其代入第二恒等式,令<script type="math/tex">v=G</script>,分别对<script type="math/tex">Q^{\prime},Q</script>积分,则</p><script type="math/tex; mode=display">\begin{aligned}\int_{\Gamma}[u(Q^{\prime})\frac{\partial G(P,Q^{\prime})}{\partial n} -G(P,Q^{\prime})\frac{\partial u(Q^{\prime})}{\partial n}]d\Gamma=\int_{\Omega}[u(Q)\Delta G(P,Q)-G(P,Q)\Delta u(Q)]d\Omega \quad\end{aligned}</script><p>  根据狄拉克<script type="math/tex">\delta</script>函数的平移特性<script type="math/tex">\int_{\Omega}f(t)\delta(t-T)dt=f(T)</script>,有</p><script type="math/tex; mode=display">\int_{\Omega}u(Q)\Delta G(P,Q)d \Omega=\int_{\Omega}u(Q)\delta(P-Q)d \Omega=u(P)</script><p>则</p><script type="math/tex; mode=display">\begin{aligned}u(P)-\int_{\Omega}G(P,Q)\Delta u(Q)d\Omega=\int_{\Gamma}[u(Q^{\prime})\frac{\partial G(P,Q^{\prime})}{\partial n} -G(P,Q^{\prime})\frac{\partial u(Q^{\prime})}{\partial n}]d\Gamma\end{aligned}</script><p>这就是第三恒等式。</p><h3 id="5-边界元-Boundary-Element-Method"><a href="#5-边界元-Boundary-Element-Method" class="headerlink" title="5. 边界元(Boundary Element Method)"></a>5. 边界元(Boundary Element Method)</h3><p>  对于第三恒等式,若<script type="math/tex">\Delta u(Q)=0</script>,则</p><script type="math/tex; mode=display">u(P)=\int_{\Gamma}[u(Q^{\prime})\frac{\partial G(P,Q^{\prime})}{\partial n} -G(P,Q^{\prime})\frac{\partial u(Q^{\prime})}{\partial n}]d\Gamma \quad P\in \Omega</script><p>这就是边界元法的内部的等式,可以看到该方法是在<script type="math/tex">\Delta u(Q)=0</script>条件下成立的。</p><p>  对于边界元法,当<script type="math/tex">P^{\prime}\in \partial \Omega</script>时,不再适用上式,因为在边界上时,若<script type="math/tex">P^{\prime}</script>与<script type="math/tex">Q^{\prime}</script>重合时,<script type="math/tex">G</script>会<strong>奇异</strong>,对于这种情况,可用半径为<script type="math/tex">\varepsilon</script>的圆包围<script type="math/tex">Q^{\prime}</script>,然后写成分段形式,并取极限<script type="math/tex">\varepsilon \rightarrow0</script>,可得到</p><script type="math/tex; mode=display">c(P^{\prime})u(P^{\prime})=\int_{\Gamma}[u(Q^{\prime})\frac{\partial G(P,Q^{\prime})}{\partial n} -G(P,Q^{\prime})\frac{\partial u(Q^{\prime})}{\partial n}]d\Gamma \quad P^{\prime}\in \partial\Omega</script><p>其中<script type="math/tex">c(P^{\prime})=\frac{\theta(P^{\prime})}{2\pi}</script>,<script type="math/tex">\theta(P^{\prime})</script>是包含<script type="math/tex">P^{\prime}</script>的内部圆弧的角度,通常对于光滑边界,<script type="math/tex">\theta(P^{\prime})=\pi</script>,则就引出我们常见边界上的形式</p><script type="math/tex; mode=display">\frac{1}{2}u(P^{\prime})=\int_{\Gamma}[u(Q^{\prime})\frac{\partial G(P,Q^{\prime})}{\partial n} -G(P,Q^{\prime})\frac{\partial u(Q^{\prime})}{\partial n}]d\Gamma \quad P^{\prime}\in \partial\Omega</script>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>math</tag>
</tags>
</entry>
<entry>
<title>Boundary Element Octahedral Fields in Volumes</title>
<link href="/2020/10/07/2020-10-07-Boundary-Element-Octahedral-Fields-in-Volumes/"/>
<url>/2020/10/07/2020-10-07-Boundary-Element-Octahedral-Fields-in-Volumes/</url>
<content type="html"><![CDATA[<h3 id="1-概括"><a href="#1-概括" class="headerlink" title="1.概括"></a>1.概括</h3><p>  将3D中的标架场描述为Octahedral Fields:三维中所有旋转构成群特殊正交群<script type="math/tex">SO(3)</script>,但由于若只用向量表示,一个标架可有24个表示,其恰构成好八面体群<script type="math/tex">O</script>,因此若想唯一的标识标架,应该为<script type="math/tex">SO(3)/O</script>,而球谐基函数恰好可为其作代数表示。不同的是采用了边界元的思想,故而在求解中输入的网格只需要边界即可,如三角形网格,而不再是四面体网格。同时对于约束问题,之前采用对每个表面法向量对齐的方式,在这松弛为表面约束积分为面积常数。采用边界元后,输入网格不再需要四面体网格,同时可通过采样获取任意精度的内部标架。不过由于奇异提取也是通过采样,因此最终获得的是采样于奇异图的”奇异点云“,而这些采样在靠近边界处非常难获得(因为需要一个loop来包围它)。</p><h3 id="2-解空间约束及松弛"><a href="#2-解空间约束及松弛" class="headerlink" title="2.解空间约束及松弛"></a>2.解空间约束及松弛</h3><p>  标架表示还是沿用球谐函数及九维向量表示。不同的是采用了边界元思想和新的约束表示。定义球谐基函数的系数集合如下,</p><script type="math/tex; mode=display">\Gamma:=\left\{c \in \mathbb{R}^{9}: c=W_{4}(R) a_{0} \text { for some } R \in \mathrm{SO}(3)\right\} \subseteq \mathbb{R}^{9}</script><p>其中<script type="math/tex">\{W\in\mathbb{R}^{9 \times 9}: W^{\top} W=I_{9 \times 9}\}</script>,为Wigner-D matrix,因此<script type="math/tex">\Gamma</script>是单位化的。为了后续目标函数的优化,松弛单位化条件,将<script type="math/tex">\Gamma</script>中的元素设置成可缩放,如下</p><script type="math/tex; mode=display">\bar{\Gamma}:=\{\beta c \in \Gamma: \beta \in \mathbb{R}\} \subseteq \mathbb{R}^{9}</script><p>不过需要注意的是<script type="math/tex">\bar{\Gamma}</script>是非线性,因此在使用边界元求解内部标架场过程中可能脱离<script type="math/tex">\bar \Gamma</script>,这意味着可能某些元素并不代表着正确的旋转,因此这时还需映射回(还是使用《Practical 3D Frame Field Generation》中的梯度下降法)<script type="math/tex">\bar{\Gamma}</script>。最后再通过单位化映射回<script type="math/tex">\Gamma</script>。</p><div class="note note-warning"> <p>  可以注意到不是所有<script type="math/tex">\left\{c \in \mathbb{R}^{9}:\|c\|_{2}=1\right\}</script>中的向量都属于解空间<script type="math/tex">\Gamma</script>,如果不松弛这个条件,优化是非线性非凸的,这个是默认松弛了,最后会用梯度下降重新投影回来,即从九维重新映射回三维。</p><p>  至于单位化这个条件也是非凸的,如果用单位化向量去优化Dirichlet energy,如果存在奇异点,能量会blow up,尤其是在细分后,这个在《Globally Optimal Direction Fields》Section 3有提到。</p><p>  所以其实这里默认松弛了两个条件。</p> </div><h3 id="3-表面特征约束及松弛"><a href="#3-表面特征约束及松弛" class="headerlink" title="3.表面特征约束及松弛"></a>3.表面特征约束及松弛</h3><p>  以上还是很抽象的解释,下面具体的约束表述。沿用过去球谐函数表示标架场的表面法向约束条件,定义如下</p><script type="math/tex; mode=display">\begin{aligned}v_{c} &:=\sqrt{\frac{5}{12}}(0, \ldots, 0,1) \\v_{s} &:=\sqrt{\frac{5}{12}(1,0, \ldots, 0)} \\v_{n} &:=\sqrt{\frac{7}{12}}(0,0,0,0,1,0,0,0,0)\\u_{\hat{n}}(\theta)&:=W_{4}\left(R_{\hat{n}}\right)\left[v_{n}+v_{c} \cos \theta+v_{s} \sin \theta\right]\end{aligned}</script><p>其中<script type="math/tex">W_{4}\left(R_{\hat{n}}\right)</script>是Wigner D-matrices,<script type="math/tex">R_{\hat{n}}</script>为将法向量旋转至Z轴的角度。进一步的,将边界约束写为如下形式,</p><script type="math/tex; mode=display">\begin{aligned}u(x):=W_{4}&\left(R_{\hat{n}(x)}\right)\left[v_{n}+v_{c} c(x)+v_{s} s(x)\right]\\&c^2(x)+s^2(x)=1\end{aligned}</script><p>  该文的关键一步——将对每个标架的约束<script type="math/tex">c^2(x)+s^2(x)=1</script>,进一步松弛为表面积分形式,</p><script type="math/tex; mode=display">\int_{\partial \Omega}\left[c(x)^{2}+s(x)^{2}\right] \mathrm{d} x=A</script><h3 id="4-能量函数"><a href="#4-能量函数" class="headerlink" title="4.能量函数"></a>4.能量函数</h3><p>  使用Dirichlet energy来平滑标架场,</p><script type="math/tex; mode=display">E[u]:=\int_{\Omega}\|\nabla u(x)\|_{2}^{2} \mathrm{d} x=\int_{\partial \Omega}u\frac{\partial u}{\partial \hat n}</script><p>上述第二个等号的由来为:假设在内部<script type="math/tex">\Delta u=0</script>,使用格林第一恒等式。至此,可将能量函数的积分区域由<script type="math/tex">\Omega</script>转换为<script type="math/tex">\partial \Omega</script>。而对于<script type="math/tex">\frac{\partial u}{\partial \hat n}</script>,如果能从边界区域<script type="math/tex">\partial \Omega</script>得到,再加上边界上的约束条件,则能量函数可被我们真正计算出来。</p><div class="note note-success"> <p>  <strong>证明:</strong>根据格林第一恒等式</p><script type="math/tex; mode=display">\int_{\Omega}(\nabla u\cdot \nabla v+u\Delta v)d\Omega=\int_\Gamma u\nabla v\cdot d\Gamma=\int_{\Gamma}u\frac{\partial v}{\partial n}d\Gamma</script><p>用<script type="math/tex">u</script>代替<script type="math/tex">v</script>,则</p><script type="math/tex; mode=display">\int_{\Omega}(\nabla u\cdot \nabla u+u\Delta u)d\Omega=\int_\Gamma u\nabla u\cdot d\Gamma=\int_{\Gamma}u\frac{\partial u}{\partial n}d\Gamma</script><p>即</p><script type="math/tex; mode=display">\int_{\Omega}||\nabla u||_2^{2}d\Omega=\int_{\Gamma}u\frac{\partial u}{\partial n}d\Gamma-\int_{\Omega}(u\Delta u)d\Omega</script><p>令<script type="math/tex">\Delta u=0</script>得到</p><script type="math/tex; mode=display">\int_{\Omega}||\nabla u||_2^{2}d\Omega=\int_{\Gamma}u\frac{\partial u}{\partial n}d\Gamma</script> </div><p>  对于<script type="math/tex">\frac{\partial u}{\partial \hat n}</script>,使用边界元的公式获得其与<script type="math/tex">u</script>的关系,</p><script type="math/tex; mode=display">\frac{1}{2} u(x)=\int_{\partial \Omega}\left[G(y-x) \frac{\partial u(y)}{\partial \hat{n}}-u(y) \frac{\partial G(y-x)}{\partial \hat{n}}\right] \mathrm{d} y \quad\quad x\in \partial \Omega</script><p>其中<script type="math/tex">G(x)</script>是格林函数<script type="math/tex">\frac{1}{4\pi||x||}</script>。</p><div class="note note-warning"> <ol><li><p>关于<script type="math/tex">\frac{\partial u}{\partial \hat n}</script>的计算是否可用<script type="math/tex">\nabla u \cdot \hat n</script>来完成?</p></li><li><p>上面在化简Dirichlet energy到边界上时使用了条件<script type="math/tex">\Delta u=0</script>,而通过该条件也可使用分部积分反推出该能量函数。是否可直接使用拉普拉斯算子来平滑场,进而用拉普拉斯矩阵来优化?<script type="math/tex">\min_{x} \sum_{i, j} w_{i j}\left\|x_{i}-x_{j}\right\|_{2}^{2}=\min _{X}tr(X^TLX)</script></p></li></ol> </div><p>  至此,能量函数归结为如下形式,</p><script type="math/tex; mode=display">\begin{array}{l}\min _{u, \frac{\partial u}{\partial \hbar}, c, s} \quad E\left[u, \frac{\partial u}{\partial \hat{n}}\right]=\int_{\Omega}\|\nabla u\|_{2}^{2}=\int_{\partial \Omega} u \frac{\partial u}{\partial \hat{n}}\\\text { s.t. }\quad u(x)=W_{4}\left(R_{\hat{n}(x)}\right)\left[v_{n}+v_{c} c(x)+v_{s} s(x)\right] \quad \forall x \in \partial \Omega \\\int_{\partial \Omega}\left[c(x)^{2}+s(x)^{2}\right] \mathrm{d} x=A \\\frac{1}{2} u(x)=\int_{\partial \Omega}\left[G(y-x) \frac{\partial u(y)}{\partial \hat{n}}-u(y) \frac{\partial G(y-x)}{\partial \hat{n}}\right] \mathrm{d} y \quad \text {holds} \quad \forall x \in \partial \Omega\end{array}</script><h3 id="5-离散化"><a href="#5-离散化" class="headerlink" title="5.离散化"></a>5.离散化</h3><p>  假设网格为三角形网格,边界有<script type="math/tex">n</script>个面。每个面上有一个<script type="math/tex">u_k \in \mathbb{R}^{9}</script>,即表面上的标架存在于面上。定义<script type="math/tex">u\in \mathbb{R}^{9n}</script>为表面上的所有未知标架;定义<script type="math/tex">\bar{u}_{m} \in \mathbb{R}^{n}</script>为所有<script type="math/tex">u_k</script>的第<script type="math/tex">m</script>个系数,。根据已有的BEM方法(C. Pozrikidis - A Practical Guide to Boundary Element Methods with the Software Library BEMLIB §5.1.4 )可获得一个矩阵<script type="math/tex">B \in \mathbb{R}^{n\times n}</script>,则<script type="math/tex">\frac{\partial \bar{u}}{\partial \hat{n}}</script>可写成如下形式</p><script type="math/tex; mode=display">B \bar{u}_{m} \approx\left(\frac{\partial \bar{u}}{\partial \hat{n}}\right)_{m}</script><p>  定义<script type="math/tex">\bar T</script>是一个关于网格单元面积的对角阵,定义<script type="math/tex">\bar L:=\bar T B</script>,在Dirichlet energy可离散化为</p><script type="math/tex; mode=display">E\left[\left\{\bar{u}_{m}\right\}_{m=-4}^{4}\right]:=\sum_{m=-4}^{4} \bar{u}_{m}^{\top} \bar{L} \bar{u}_{m}=u^{\top} L u</script><p>其中<script type="math/tex">L \in \mathbb{R}^{9n\times 9n}</script>由<script type="math/tex">\bar L</script>这些分块组成,进一步如果<script type="math/tex">L</script>不对称,使其对称化,<script type="math/tex">L \leftarrow \frac{1}{2}\left(L+L^{\top}\right)</script>。(作者称<script type="math/tex">L</script>为半正定)</p><p>  定义<script type="math/tex">c,s \in \mathbb{R}^n</script>是边界上的约束,<script type="math/tex">u_0\in \mathbb{R}^{9n}</script>由<script type="math/tex">W_{4}\left(R_{\hat{n}_k}\right)v_n</script>分块组成,同时定义约束矩阵<script type="math/tex">H_c,H_s\in \mathbb{R}^{9n\times n}</script>分别为由<script type="math/tex">W_{4}\left(R_{\hat{n}_{k}}\right) v_{c} c_{k}</script>和<script type="math/tex">W_{4}\left(R_{\hat{n}_{k}}\right) v_{s} s_{k}</script>分块组成,则表面标架和表面法向约束变为</p><script type="math/tex; mode=display">u=u_{0}+H_{c} c+H_{s} s\\c^{\top} \bar{T} c+s^{\top} \bar{T} s=A</script><p>  至此,总的优化目标变为</p><script type="math/tex; mode=display">\begin{aligned}\min _{\{u, c, s\}} & u^{\top} L u \\\text { s.t. } & u=u_{0}+H_{c} c+H_{s} s \\& c^{\top} \bar{T} c+s^{\top} \bar{T} s=A\end{aligned}</script><p>共有<script type="math/tex">9n+2n=11n</script>个变量,<script type="math/tex">9n+1</script>个约束,所以自由度<script type="math/tex">2n-1</script>。</p><h3 id="6-优化"><a href="#6-优化" class="headerlink" title="6.优化"></a>6.优化</h3><p>  定义如下矩阵</p><script type="math/tex; mode=display">\begin{array}{l}Q:=\left(\begin{array}{cc}H_{c}^{\top} L H_{c} & H_{c}^{\top} L H_{s} \\H_{s}^{\top} L H_{c} & H_{s}^{\top} L H_{s}\end{array}\right) \in \mathbb{R}^{2 n \times 2 n} \\v:=\left(\begin{array}{c}H_{c}^{\top} L u_{0} \\H_{s}^{\top} L u_{0}\end{array}\right) \in \mathbb{R}^{2 n} \\x:=\left(\begin{array}{c}c \\s\end{array}\right) \in \mathbb{R}^{2 n} \\T:=\operatorname{diag}(\bar{T}, \bar{T}) \in \mathbb{R}^{2 n \times 2 n}\end{array}</script><p>对于前一步得到的矩阵形式优化目标,将<script type="math/tex">u</script>代入能量函数,转置标量,则优化目标变可变为</p><script type="math/tex; mode=display">\begin{array}{rl}\min _{x \in \mathbb{R}^{2 n}} & x^{\top} Q x+2 x^{\top} v \\\text { s.t. } & x^{\top} T x=A\end{array}</script><p>  使用拉格朗日乘子法得到</p><script type="math/tex; mode=display">(Q-\lambda T) x=-v</script><p>将<script type="math/tex">x</script>看成关于<script type="math/tex">\lambda</script>的函数<script type="math/tex">x(\lambda)</script>,定义<script type="math/tex">g(\lambda):=x(\lambda)^{\top} T x(\lambda)-A</script>,则上述满足优化目标的乘子应为<script type="math/tex">g(\lambda)=0</script>的根,可使用二分法获得。</p><div class="note note-success"> <p>  <strong>证明</strong>:对于方程<script type="math/tex">(Q-\lambda T) x=-v</script>,假设<script type="math/tex">Q-\lambda T</script>不可逆,则必有<script type="math/tex">x</script>使</p><script type="math/tex; mode=display">\begin{aligned}(Q-\lambda T) x&=0\\Qx&=\lambda Tx\end{aligned}</script><p>问题转化为广义特征值问题,设<script type="math/tex">\lambda_{min}</script>是其最小特征值。假设特征向量不正交于方程右边<script type="math/tex">v</script>,则对于方程左边,当<script type="math/tex">\lambda \rightarrow \lambda_{\min }</script>时,<script type="math/tex">\|x\| \rightarrow \infty</script>,即<script type="math/tex">\lim _{\lambda \rightarrow \lambda_{\min }} g(\lambda)=\infty</script>。同样的,当<script type="math/tex">\lambda \rightarrow -\infty</script>时,<script type="math/tex">\|x\| \rightarrow 0</script>,即</p><p><script type="math/tex">\lim _{\lambda \rightarrow-\infty} g(\lambda)=-A</script>。又因为<script type="math/tex">g^{\prime}(\lambda)>0</script>,则在<script type="math/tex">\lambda\in(-\infty,\lambda_{min})</script>之间必定存在一个根使<script type="math/tex">g(\lambda) =0</script>。</p> </div><p>  经上述步骤,优化的解为<script type="math/tex">x(\lambda_0)</script>,其中<script type="math/tex">g(\lambda_0) =0</script>。第一步求解<script type="math/tex">\lambda_{min}</script>时间很快,花费最大的是在第二部使用二分法求根(每次都要对一个稠密线性系统逆运算)。</p><h3 id="7-内部场"><a href="#7-内部场" class="headerlink" title="7.内部场"></a>7.内部场</h3><p>  在求解内部场是可先单位化场<script type="math/tex">\frac{u(x)}{||u(x)||_2},x\in \partial \Omega</script>,在优化时是不能单位化的,不过在插值生成内部场时不同大小的<script type="math/tex">u</script>会造成坏的影响,比如<script type="math/tex">u(x)</script>越大,标架对于内部接近边界的奇异点的选择会更加显著。内部标架场可由边界元的公式获得,</p><script type="math/tex; mode=display">u(x)=\int_{\partial \Omega}\left[G(y-x) \frac{\partial u(y)}{\partial \hat{n}}-u(y) \frac{\partial G(y-x)}{\partial \hat{n}}\right] \mathrm{d} y \quad \quad x \in \Omega \backslash \partial\Omega</script><p>对于每个三角形单元,定义如下</p><script type="math/tex; mode=display">\begin{array}{l}\sigma_{k}(x):=\int_{T_{k}} G(y-x) \mathrm{d} y \\\rho_{k}(x):=\hat{n}_{k} \cdot \int_{T_{k}} \nabla G(y-x) \mathrm{d} y\end{array}</script><p>则</p><script type="math/tex; mode=display">u(x)=\sum_{k}\left[u_{k} \sigma_{k}(x)+[B u]_{k} \rho_{k}(x)\right]\\</script><div class="note note-warning"> <p>这边貌似转换存在问题?按照定义应该是</p><script type="math/tex; mode=display">u(x)=\sum_{k}\left[ \sigma_{k}(x)[B u]_k-u_{k}\rho_{k}(x)\right]</script><p>否则,按照他给定的式子,则原式变成了</p><script type="math/tex; mode=display">u(x)=\int_{\partial \Omega}\left[u(y)G(y-x)+ \frac{\partial u(y)}{\partial \hat{n}} \frac{\partial G(y-x)}{\partial \hat{n}}\right] \mathrm{d} y \quad \quad x \in \Omega \backslash \partial\Omega</script> </div><p>  解出<script type="math/tex">u</script>后,<script type="math/tex">u</script>可能不在解空间<script type="math/tex">\bar\Gamma</script>中,使用《Practical 3D Frame Field Generation》中梯度下降法(closest_frame)映射回<script type="math/tex">\bar \Gamma</script>再单位化。</p><div class="note note-warning"> <p>  这里的closest_frame算法用的是<script type="math/tex">L_2</script>模,即欧式距离,能保证在<script type="math/tex">\Gamma</script>中欧式距离能给出很好的测度吗? (<script type="math/tex">\Gamma</script>是一个spherical 3- manifold)</p> </div><h3 id="8-奇异结构"><a href="#8-奇异结构" class="headerlink" title="8.奇异结构"></a>8.奇异结构</h3><p>  奇异结构通过采样细分,对内部采样三个点,若内部有奇异点,则四等分三角形,继续检测奇异点。</p><center><img src="/2020/10/07/2020-10-07-Boundary-Element-Octahedral-Fields-in-Volumes/image-20201009135034188.png" class="" title="image-20201009135034188"></center><h3 id="9-感想"><a href="#9-感想" class="headerlink" title="9.感想"></a>9.感想</h3><ul><li>之前几篇(包括这篇)的约束都在表面,而内部的奇异结构对六面体生成也是有影响的,因为这也是六面体相比于四面体网格难生成的原因(比如《Singularity-Constrained Octahedral Fields for Hexahedral Meshing》就有提到内部奇异结构的影响)。所以若要考虑六面体生成,仅保持表面约束法向约束是不是只是必要条件?</li><li>在求解标架场时,遇到的问题其一是非线性非凸约束(约束住<script type="math/tex">u</script>所代表的是基函数旋转后的系数),其二是得到的解需要映射回三维。对于一,大多是松弛约束,对于二,大多是采用梯度下降逆向求解。是否有更好的优化方法而不需要松弛约束和映射? </li></ul>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>标架场</tag>
</tags>
</entry>
<entry>
<title>Practical 3D Frame Field Generation-3D</title>
<link href="/2020/09/17/2020-09-17-Practical-3D-Frame-Field-Generation-3D/"/>
<url>/2020/09/17/2020-09-17-Practical-3D-Frame-Field-Generation-3D/</url>
<content type="html"><![CDATA[<p>  作者在前文的2d形式下引入的基函数其实是傅里叶基底,而在3d下,根据《Boundary aligned smooth 3D cross-frame field》的启发,使用了球谐函数作为基函数。不同的是该文使用了不同的多项式<script type="math/tex">x^4+y^4+z^4</script>来在球上展开。(而前文是<script type="math/tex">x^2y^2+y^2z^2+z^2x^2</script>,其实优化后式子是一样的)</p><h3 id="二、3D"><a href="#二、3D" class="headerlink" title="二、3D"></a>二、3D</h3><h4 id="1-标架表示"><a href="#1-标架表示" class="headerlink" title="1. 标架表示"></a>1. 标架表示</h4><p>  如果<script type="math/tex">\tilde{F}</script>为参考标架函数,那么它为:</p><script type="math/tex; mode=display">\begin{aligned}\tilde{F}&=\sqrt{\frac{7}{12}}Y_{4}^{0}+\sqrt{\frac{5}{12}}Y_{4}^{4}\\&=B\tilde{a}\\&=\left(Y_{4}^{-4},Y_{4}^{-3},Y_{4}^{-2},Y_{4}^{-1}, Y_{4}^{0}, Y_{4}^{1},Y_{4}^{2},Y_{4}^{3}, Y_{4}^{4}\right)\left(0,0,0,0, \sqrt{\frac{7}{12}}, 0,0,0, \sqrt{\frac{5}{12}}\right)^{\top}\end{aligned}</script><p>其中<script type="math/tex">Y_{l}^{m}</script>是实球谐基函数,其中<script type="math/tex">l</script>称为degree/band,<script type="math/tex">m</script>称为order,<script type="math/tex">m\in[-l,l]</script>。</p><p>(球谐函数在图形学中先是被用于光照渲染《<em>Spherical harmonic lighting: The gritty details</em>》,<a href="https://zhuanlan.zhihu.com/p/51179426">一个通俗的介绍球谐函数的系列文章</a>)</p><p>ps:为什么选择球谐函数?因为它具有以下性质:</p><ul><li><p>标准正交性,它是一组正交基</p></li><li><p>旋转不变性,比如用矩阵<script type="math/tex">R</script>旋转<script type="math/tex">f(x)</script>后变为<script type="math/tex">g(x)</script>,即<script type="math/tex">g(x)=Rf(x)</script>,根据旋转不变性有<script type="math/tex">g(x)=f(Rx)</script></p></li><li><p><strong>函数乘积的积分等于其球谐系数向量的点积</strong>,<script type="math/tex">\int_{S} L(s) t(s) d s=\sum_{i=0}^{\infty} L_{i} t_{i}</script></p></li></ul><p>由旋转不变性,任意标架可表示为<script type="math/tex">F=BR\tilde{a}</script>,其中<script type="math/tex">R</script>是<script type="math/tex">9\times 9</script>旋转矩阵,可由Wigner D-matrices 给出。</p><h4 id="2-能量函数"><a href="#2-能量函数" class="headerlink" title="2. 能量函数"></a>2. 能量函数</h4><script type="math/tex; mode=display">E=\sum_{i j} \int_{S^{2}}\left(F^{j}(\alpha)-F^{i}(\alpha)\right)^{2} d \alpha=\sum_{i j}\left\|a^{j}-a^{i}\right\|^{2}</script><p>相比于2d,3d中的能量函数其实是类似的,不同的是<script type="math/tex">a</script>,不再相同,因为引入了球谐函数作为基函数。可以注意到<script type="math/tex">a</script>是一个九维向量,而我们要得到的是一个三维向量(即<script type="math/tex">a</script>是一个嵌入到九维空间的三维流形),所以后面还需要用其他方法求解其投影在三维中对应的向量,文章中赋予了一个随机初值并使用梯度下降来求解(We do not have a formal proof, but we conjecture that there is a single minimum of the L2 norm )</p><h4 id="3-约束"><a href="#3-约束" class="headerlink" title="3. 约束"></a>3. 约束</h4><p>  一些顶点上的标架需要满足约束条件,如下</p><h5 id="3-1法向量对齐z轴"><a href="#3-1法向量对齐z轴" class="headerlink" title="3.1法向量对齐z轴"></a>3.1法向量对齐z轴</h5><p>  根据<script type="math/tex">a=R_z\tilde{a}</script>可得到</p><script type="math/tex; mode=display">a=\left(\sqrt{\frac{5}{12}} \sin 4 \theta, 0,0,0, \sqrt{\frac{7}{12}}, 0,0,0, \sqrt{\frac{5}{12}} \cos 4 \theta\right)^{\top}</script><p>其中<script type="math/tex">R_z</script>是绕z轴的旋转矩阵。引入向量<script type="math/tex">c=(c_0,c_1)</script>,可写成以下形式</p><script type="math/tex; mode=display">\begin{aligned}a &=\sqrt{\frac{7}{12}}(0,0,0,0,1,0,0,0,0)^{\top} \\&+c_{0}(0,0,0,0,0,0,0,0,1)^{\top} \\&+c_{1}(1,0,0,0,0,0,0,0,0)^{\top}\end{aligned}</script><p>其中<script type="math/tex">cc^{\top}=\frac{5}{12}</script></p><h5 id="3-2法向量不与z轴对齐"><a href="#3-2法向量不与z轴对齐" class="headerlink" title="3.2法向量不与z轴对齐"></a>3.2法向量不与z轴对齐</h5><p>  策略是先将z轴转到法向量方向,即乘一个旋转矩阵<script type="math/tex">R</script>,则<script type="math/tex">a</script>变成以下形式</p><script type="math/tex; mode=display">\begin{aligned}a &=\sqrt{\frac{7}{12}}R(0,0,0,0,1,0,0,0,0)^{\top} \\& +c_{0}R(0,0,0,0,0,0,0,0,1)^{\top} \\& +c_{1}R(1,0,0,0,0,0,0,0,0)^{\top}\\\end{aligned}</script><h5 id="3-3尖锐、角点"><a href="#3-3尖锐、角点" class="headerlink" title="3.3尖锐、角点"></a>3.3尖锐、角点</h5><p>  该类顶点上的标架需要符合两个法向量,选择两个大致正交的法向量作为它的特征,然后通过旋转让它们正交,然后将z轴转到它们的叉积方向,方法同3.2</p><h5 id="3-4-旋转矩阵"><a href="#3-4-旋转矩阵" class="headerlink" title="3.4 旋转矩阵"></a>3.4 旋转矩阵</h5><p>  旋转矩阵为9x9矩阵,为Wigner D-matrices </p><script type="math/tex; mode=display">\begin{aligned}&R_{B}^{z}(\gamma)=\left[\begin{array}{ccccccccc}\cos (4 \gamma) & 0 & 0 & 0 & 0 & 0 & 0 & 0 & \sin (4 \gamma) \\0 & \cos (3 \gamma) & 0 & 0 & 0 & 0 & 0 & \sin (3 \gamma) & 0 \\0 & 0 & \cos (2 \gamma) & 0 & 0 & 0 & \sin (2 \gamma) & 0 & 0 \\0 & 0 & 0 & \cos (\gamma) & 0 & \sin (\gamma) & 0 & 0 & 0 \\0 & 0 & 0 &0 & 1 & 0 & 0 & 0 & 0 \\0 & 0 & 0 & -\sin (\gamma) & 0 & \cos ( \gamma) & 0 & 0 & 0 & 0 \\0 & 0 & -\sin (2 \gamma) & 0 & 0 & 0 & \cos (2 \gamma) & 0 & 0 & 0 \\0 & -\sin (3 \gamma) & 0 & 0 & 0 & 0 & 0 & \cos (3 \gamma) & 0 \\-\sin (4 \gamma) & 0 & 0 & 0 & 0 & 0 & 0 & 0 & \cos (4 \gamma)\end{array}\right]\end{aligned}</script><script type="math/tex; mode=display">\begin{aligned}&R_{B}^{x}(\pi / 2)=\left[\begin{array}{cccccccc}0 & 0 & 0 & 0 & 0 & \sqrt{14} / 4 & 0 & -\sqrt{2} / 4 & 0 \\0 & -3 / 4 & 0 & \sqrt{7} / 4 & 0 & 0 & 0 & 0 & 0 \\0 & 0 & 0 & 0 & 0 & \sqrt{2} / 4 & 0 & \sqrt{14} / 4 & 0 \\0 & \sqrt{7} / 4 & 0 & 3 / 4 & 0 & 0 & 0 & 0 & 0 \\0 & 0 & 0 & 0 & 3 / 8 & 0 & \sqrt{5} / 4 & 0 & \sqrt{35} / 8 \\-\sqrt{14} / 4 & 0 & -\sqrt{2} / 4 & 0 & 0 & 0 & 0 & 0 & 0 \\0 & 0 & 0 & 0 & \sqrt{5} / 4 & 0 & 1 / 2 & 0 & -\sqrt{7} / 4 \\\sqrt{2} / 4 & 0 & -\sqrt{14} / 4 & 0 & 0 & 0 & 0 & 0 & 0 \\0 & 0 & 0 & 0 & \sqrt{35} / 8 & 0 & -\sqrt{7} / 4 & 0 & 1 / 8\end{array}\right]\end{aligned}</script><script type="math/tex; mode=display">\begin{array}{l}R_{B}^{y}(\beta)=R_{B}^{x}(\pi / 2) \times R_{B}^{z}(\beta) \times R_{B}^{x}(\pi / 2)^{\top} \\R_{B}^{x}(\alpha)=R_{B}^{y}(\pi / 2)^{\top} \times R_{B}^{z}(\alpha) \times R_{B}^{y}(\pi / 2)\end{array}</script><p>则最终旋转矩阵为</p><script type="math/tex; mode=display">R_{B}(\alpha, \beta, \gamma)=R_{B}^{x}(\alpha) \times R_{B}^{y}(\beta) \times R_{B}^{z}(\gamma)</script><h4 id="4-矩阵化"><a href="#4-矩阵化" class="headerlink" title="4.矩阵化"></a>4.矩阵化</h4><p>  求解采用最小二乘<script type="math/tex">||AX-b||</script>,其中标架<script type="math/tex">a^i</script>和约束<script type="math/tex">c^i</script>存储于<script type="math/tex">X</script>向量(<script type="math/tex">(9n_v+2n_l)</script>维,<script type="math/tex">n_v</script>为不带约束的顶点,<script type="math/tex">n_l</script>是带约束的顶点)</p><script type="math/tex; mode=display">\begin{aligned}X[9i+d]=a_d^i ,\quad \quad i \in[0,n_v],d \in [0,8]\\ X[9n_v+2i+d]=c_{d}^{i},\quad \quad i \in [0,n_l],d \in [0,1]\\\end{aligned}</script><p>  系数矩阵<script type="math/tex">A_{(9\mathcal{E}+9n_l)\times(9n_v+2n_l)}</script>(<script type="math/tex">\mathcal{E}</script>为边数)下标是行,上标是列,设置如下:</p><script type="math/tex; mode=display">A_{9\mathcal{E}\times (9n_v+2n_l)}=\left\{\begin{aligned}&a_{\mathcal{E}_{ij}+d}^{9i+d}=1,a_{\mathcal{E}_{ij}+d}^{9j+d}=-1 &&edge(v_i,v_j)\in \mathcal{E}, d \in[0,8]\\&0 &&other\end{aligned}\right.</script><p>注意到上面的<script type="math/tex">A</script>还不完整,还需在下面加入一个描述约束的block(<script type="math/tex">9n_l\times(9n_v+2n_l)</script>)</p><p>  对于有约束点,计算得到其将z轴旋转到法向方向的9x9旋转矩阵<script type="math/tex">R_B</script>,并定义如下值</p><script type="math/tex; mode=display">\begin{aligned}h_{0} & = R_{B} (1,0,0,0,0,0,0,0,0)^{\top} \\h_{4} & = R_{B} (0,0,0,0,1,0,0,0,0)^{\top} \\h_{8} &= R_{B} (0,0,0,0,0,0,0,0,1)^{\top}\\\lambda& =100\end{aligned}</script><p>其中<script type="math/tex">\lambda</script>是惩罚系数,则矩阵A剩余部分为</p><script type="math/tex; mode=display">A_{9n_l\times(9n_v+2n_l)}=\left\{\begin{aligned}&a_{i+d}^{9i+d}=\lambda,\quad a_{i+d}^{9 n_v+2i}=\lambda h_0[d],\quad a_{i+d}^{9n_v+2i+1}=\lambda h_8[d] \ \ \ &&i\in \mathcal{n_l}, d \in[0,8]\\&0 &&other\end{aligned}\right.</script><p>  <script type="math/tex">b</script>为<script type="math/tex">(9\mathcal{E}+9n_l)</script>维向量,其前<script type="math/tex">9\mathcal{E}</script>维为<script type="math/tex">0</script>,后<script type="math/tex">9n_l</script>由约束决定,如下</p><script type="math/tex; mode=display">b_{9\mathcal{E}+9n_l}=\left\{\begin{aligned}&b_i=0 \quad && i \in[0,9\mathcal{E})\\&b_i=\lambda \sqrt{\frac{7}{12}}h_4[d] &&i \in[9\mathcal{E},9\mathcal{E}+9n_l]\end{aligned}\right.</script><p>注意到<script type="math/tex">b</script>中的非零项中的<script type="math/tex">h_4</script>和<script type="math/tex">A</script>中的约束项一一对应的,即矩阵<script type="math/tex">A</script>和<script type="math/tex">b</script>都是动态生成的,分成了两个block:表面约束点和自由点。另据作者的说法,在这两个block中做一个Hilbert sort会提升大约30%的效率。</p><p>  至此,可通过求解<script type="math/tex">A^{\top}AX=A^{\top}X</script>得到位于顶点的嵌入到九维空间的标架表示(不再像2D中那样只需标准化就行了)。</p>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>标架场</tag>
</tags>
</entry>
<entry>
<title>Boundary Aligned Smooth 3D Cross-Frame Field</title>
<link href="/2020/09/16/2020-09-16-Boundary-Aligned-Smooth-3D-Cross-Frame-Field/"/>
<url>/2020/09/16/2020-09-16-Boundary-Aligned-Smooth-3D-Cross-Frame-Field/</url>
<content type="html"><![CDATA[<h3 id="标架表示方法"><a href="#标架表示方法" class="headerlink" title="标架表示方法"></a>标架表示方法</h3><p>  3d标架存在24个方向(坐标轴有6个选择,垂直它的有4种,即6x4=24种),为了表述两个相邻标架的差异,采用基函数来表示<strong>四面体网格面上的标架</strong>,而不是采用以前的使用vector pair来表示(避免了混合整数规划问题),该文首先提出了采用球谐函数作为基函数来描述标架,从而可以通过积分来描述标架的差异并据此定义能量函数来优化 ,最后使用L-BFGS来光滑标架场,该方法同时支持表面法向量约束对齐。</p><h4 id="1-标架表示"><a href="#1-标架表示" class="headerlink" title="1. 标架表示"></a>1. 标架表示</h4><p>  该文中用<script type="math/tex">h(t)=t_x^2t_y^2+t_y^2t_z^2+t_z^2t_x^2</script>来表示标架,将分解成球谐基函数表示</p><script type="math/tex; mode=display">f_{[I]}=-\frac{2 \sqrt{\pi}}{15}\left(Y_{4}^{0}+\sqrt{\frac{5}{7}} Y_{4}^{4}+16 \sqrt{\pi} Y_{0}^{0}\right)</script><p>考虑比较比较两标架的差异时无需常数项,进而简化成</p><script type="math/tex; mode=display">f_{[I]}=\sqrt{7}Y_{4}^{0}+\sqrt{5} Y_{4}^{4}</script><p>又由球谐函数的旋转不变性,则</p><script type="math/tex; mode=display">f_{[R]}(s)=h(R^{\top}s)</script><p>其中R是旋转矩阵。进入可用以下目标函数表示两标架之间的差异</p><script type="math/tex; mode=display">\int_{S^{2}}\left(f_{\left[R_{a}\right]}(s)-f_{\left[R_{b}\right]}(s)\right)^{2} d s=\left\|\hat{R}_{a} \hat{h}-\hat{R}_{b} \hat{h}\right\|^{2}</script><h4 id="2-约束"><a href="#2-约束" class="headerlink" title="2.约束"></a>2.约束</h4><p>  表面约束为标架方向之一与法向量对齐,作者证明了当且仅当<script type="math/tex">\hat{f}(4)=\sqrt{7}</script>,即参考标架向量的第四个系数为常数,进而约束可分为两步:1.旋转法向量到z轴(需要旋转矩阵);2.对齐法向量</p><h4 id="3-优化目标"><a href="#3-优化目标" class="headerlink" title="3.优化目标"></a>3.优化目标</h4><p>  作者为了平滑效果和避免因网格密度造成的不均匀采样,将优化目标转换成了球谐函数的系数的梯度。</p><script type="math/tex; mode=display">\nabla \hat{f}(k)=\sum_{i=1}^{4} \hat{f}_{p_{i}}(k) \nabla w_{i}</script><p>以上通过四面体网格面上的标架导出四面体标架系数的梯度,其中<script type="math/tex">w_i</script>是四面体每个面的重心的梯度(<a href="https://zhuanlan.zhihu.com/p/162939718">具体计算方法</a>)</p><p>  通过体积分计算全局的量,</p><script type="math/tex; mode=display">E_{s}\left(\mathrm{TET}_{j}\right)=E_{s}(p)=\sum_{k=0}^{8}\|\nabla \hat{f}(k)\|^{2}, p \in \mathrm{TET}_{j}</script><p>  通过面积分计算表面约束,</p><script type="math/tex; mode=display">\begin{aligned}E_{a} &=\int_{\partial \Omega}\left\|\left(\hat{R}_{n_{p} \rightarrow z} \hat{f}_{p}\right)(4)-\sqrt{7}\right\|^{2} d p \\& \approx \sum_{\mathrm{TRI}_{i} \in \partial \Omega} \operatorname{area}\left(\mathrm{TRI}_{i}\right)\left\|\left(\hat{R}_{n_{i} \rightarrow z} \hat{f}_{p_{i}}\right)(4)-\sqrt{7}\right\|^{2}\end{aligned}</script><p>考虑到体积分和面积分造成的权重不一致,则总的能量函数为</p><script type="math/tex; mode=display">E_{f}=\frac{E_{s}}{\operatorname{volume}(\Omega)^{1 / 3}}+w_{a} \frac{E_{a}}{\operatorname{area}(\partial \Omega)}</script><p>其中<script type="math/tex">w_a</script>是对齐约束的惩罚系数。</p><p>  至此解出<script type="math/tex">\arg \min_{\hat f}{E_f(\hat{f})}</script>,但是它是一个嵌入在九维空间的三维流形,需将它投影到三维,生成一个初始旋转<script type="math/tex">\Phi_{i}</script>,将其映射回九维,并作<script type="math/tex">L^2</script>范数的优化,得到<script type="math/tex">\hat f</script>在三维的近似表示</p><script type="math/tex; mode=display">\Phi_{0, i} \leftarrow \arg \min _{\Phi_{i}}\left\|\hat{f}_{0, i}-\hat{R}\left(\Phi_{i}\right) \hat{h}\right\|^{2}</script><p>最后使用L-BFGS迭代使 标架场光滑<script type="math/tex">\arg \min _{\Phi} E_{f}\left(\hat{f}_{[R(\Phi)]}\right)</script></p>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>标架场</tag>
</tags>
</entry>
<entry>
<title>Paper Introduction</title>
<link href="/2020/09/16/2020-09-16-Paper-Introduction/"/>
<url>/2020/09/16/2020-09-16-Paper-Introduction/</url>
<content type="html"><![CDATA[<h3 id="1-标架场"><a href="#1-标架场" class="headerlink" title="1. 标架场"></a>1. 标架场</h3><h4 id="《Instant-field-aligned-meshes》-2015"><a href="#《Instant-field-aligned-meshes》-2015" class="headerlink" title="《Instant field-aligned meshes》-2015"></a>《Instant field-aligned meshes》-2015</h4><p>  采用局部的高斯赛德尔迭代来求解表面网格的标架场和位置场,其在局部直接暴力比较(搜索空间其实很小,以4-Rosy为例,只有16)两个标架的不同来进行优化,并用<strong>多分辨率层次法(Multiresolution hierarchy)</strong>来避免陷入局部解,该方法<strong>速度快</strong>,<strong>鲁棒性好</strong>。<a href="https://senjay.github.io/2020/08/22/2020-08-22-Instant-Meshes-%E6%A0%87%E6%9E%B6%E5%9C%BA%E6%96%B9%E6%B3%95/">笔记1</a>,<a href="https://senjay.github.io/2020/08/28/2020-02-22-Instant-Meshes-%E5%A5%87%E5%BC%82%E7%82%B9%E6%A3%80%E6%B5%8B/">笔记2</a></p><p>Wenzel Jakob et al., “Instant Field-Aligned Meshes,” <em>ACM Transactions on Graphics</em> 34, no. 6 (November 4, 2015): 1–15, <a href="https://doi.org/10.1145/2816795.2818078">https://doi.org/10.1145/2816795.2818078</a>.</p><h4 id="《LoopyCuts-practical-feature-preserving-block-decomposition》-2020"><a href="#《LoopyCuts-practical-feature-preserving-block-decomposition》-2020" class="headerlink" title="《LoopyCuts: practical feature-preserving block decomposition》-2020"></a>《<i>LoopyCuts</i>: practical feature-preserving block decomposition》-2020</h4><p>  一阶段首先通过MIQ方法生成的标架场和确定好的特征来生成Field-Coherent Loops(方法采用《Tracing Field-Coherent Quad Layouts》),提出通过维护采样池和采样最远loop队列来对网格进行loop划分;二阶段通过划分好的loop通过中点细分生成体网格。(该方法对标架场质量要求高,速度慢,一些demo我测试没通过)<a href="https://senjay.github.io/2020/08/16/LoopyCuts%E7%BC%96%E8%AF%91%E8%BF%90%E8%A1%8C%E6%96%B9%E6%B3%95/">编译方法</a>,<a href="https://senjay.github.io/2020/09/04/2020-09-04-LoopyCuts-Computer-Cutting-Loops/">笔记</a></p><p>Livesu, Marco, Nico Pietroni, Enrico Puppo, Alla Sheffer, and Paolo Cignoni. “<em>LoopyCuts</em>: Practical Feature-Preserving Block Decomposition for Strongly Hex-Dominant Meshing.” <em>ACM Transactions on Graphics</em> 39, no. 4 (July 8, 2020). <a href="https://doi.org/10.1145/3386569.3392472">https://doi.org/10.1145/3386569.3392472</a>.</p><h4 id="《Boundary-Aligned-Smooth-3D-Cross-Frame-Field》-2011"><a href="#《Boundary-Aligned-Smooth-3D-Cross-Frame-Field》-2011" class="headerlink" title="《Boundary Aligned Smooth 3D Cross-Frame Field》-2011"></a>《Boundary Aligned Smooth 3D Cross-Frame Field》-2011</h4><p>  3d标架存在24个方向(坐标轴有6个选择,垂直它的有4种,即6x4=24种),为了表述两个相邻标架的差异,采用基函数来表示<strong>四面体网格面上的标架</strong>,而不是采用以前的使用vector pair来表示,该文首先提出了采用球谐函数作为基函数来描述标架,从而可以通过积分来描述标架的差异并据此定义能量函数来优化 ,最后使用L-BFGS来光滑标架场,该方法同时支持表面法向量约束对齐(有缺陷)。并通过3d标架来引导出流线划分网格并生成hexahedron-dominant mesh。(计算量大)</p><p>Huang, Jin, Yiying Tong, Hongyu Wei, and Hujun Bao. “Boundary Aligned Smooth 3D Cross-Frame Field.” In <em>Proceedings of the 2011 SIGGRAPH Asia Conference on - SA ’11</em>, 1. Hong Kong, China: ACM Press, 2011. <a href="https://doi.org/10.1145/2024156.2024177">https://doi.org/10.1145/2024156.2024177</a>.</p><h4 id="《Practical-3D-frame-field-generation》-2016"><a href="#《Practical-3D-frame-field-generation》-2016" class="headerlink" title="《Practical 3D frame field generation》-2016"></a>《Practical 3D frame field generation》-2016</h4><p>  先描述2D形式下使用基函数来描述标架场的可行性,进而拓展到3D采用球谐函数作为基函数来表述<strong>四面体网格顶点上的标架</strong>,该文的优化方法相比于《Boundary Aligned Smooth 3D Cross-Frame Field》性能更好,同时增加了约束限制条件的可行性。该文先以一个简化的方法来给出一个目标能量函数,并用最小二乘来初始化它,最后也已L-BFGS法来平滑。</p><p>Nicolas Ray, Dmitry Sokolov, and Bruno Lévy, “Practical 3D Frame Field Generation,” <em>ACM Transactions on Graphics</em> 35, no. 6 (November 11, 2016): 1–9, <a href="https://doi.org/10.1145/2980179.2982408">https://doi.org/10.1145/2980179.2982408</a>.</p><p>Ray, Nicolas, and Dmitry Sokolov. “On Smooth 3D Frame Field Design.” <em>ArXiv:1507.03351 [Cs]</em>, July 13, 2015. <a href="http://arxiv.org/abs/1507.03351">http://arxiv.org/abs/1507.03351</a>.</p><h4 id="《Boundary-Element-Octahedral-Fields-in-Volumes》-2017"><a href="#《Boundary-Element-Octahedral-Fields-in-Volumes》-2017" class="headerlink" title="《Boundary Element Octahedral Fields in Volumes》-2017"></a>《Boundary Element Octahedral Fields in Volumes》-2017</h4><p>  将3D中的标架场描述为Octahedral Fields:三维中所有旋转构成群特殊正交群<script type="math/tex">SO(3)</script>,但由于若只用向量表示,一个标架可有24个表示,其恰构成好八面体群<script type="math/tex">O</script>,因此若想唯一的标识标架,应该为<script type="math/tex">SO(3)/O</script>,而球谐基函数恰好可为其作代数表示。不同的是采用了边界元的思想,故而在求解中输入的网格只需要边界即可,如三角形网格,而不再是四面体网格。同时对于约束问题,之前采用对每个表面法向量对齐的方式,在这松弛为表面约束积分为面积常数。采用边界元后,输入网格不再需要四面体网格,同时可通过采样获取任意精度的内部标架。不过由于奇异提取也是通过采样,因此最终获得的是采样于奇异图的”奇异点云“,而这些采样在靠近边界处非常难获得(因为需要一个loop来包围它)。</p><p>Solomon, Justin, Amir Vaxman, and David Bommes. “Boundary Element Octahedral Fields in Volumes.” <em>ACM Transactions on Graphics</em> 36, no. 3 (July 6, 2017): 1–16. <a href="https://doi.org/10.1145/3065254">https://doi.org/10.1145/3065254</a>.</p><h3 id="2-网格简化"><a href="#2-网格简化" class="headerlink" title="2.网格简化"></a>2.网格简化</h3><h4 id="《Surface-simplification-using-quadric-error-metrics》-1997"><a href="#《Surface-simplification-using-quadric-error-metrics》-1997" class="headerlink" title="《Surface simplification using quadric error metrics》-1997"></a>《Surface simplification using quadric error metrics》-1997</h4><p>  通过Quadratic error Matrix来描述距离误差,进而塌陷边到合适的点来达到简化网格的目的。<a href="https://senjay.github.io/2020/07/14/2020-07-14-Quadric-error-metric/">笔记</a></p><p>Michael Garland and Paul S. Heckbert, “Surface Simplification Using Quadric Error Metrics,” in <em>Proceedings of the 24th Annual Conference on Computer Graphics and Interactive Techniques - SIGGRAPH ’97</em> (the 24th annual conference, Not Known: ACM Press, 1997), 209–16, <a href="https://doi.org/10.1145/258734.258849">https://doi.org/10.1145/258734.258849</a>.</p>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>论文简介</tag>
</tags>
</entry>
<entry>
<title>Practical 3D Frame Field Generation-2D</title>
<link href="/2020/09/14/2020-09-14-Practical-3D-Frame-Field-Generation-2D/"/>
<url>/2020/09/14/2020-09-14-Practical-3D-Frame-Field-Generation-2D/</url>
<content type="html"><![CDATA[<p>  该论文以基函数来描述标架,首先说明了该方法在2D下的可行性,然后拓展到3D形式。3D形式延续《Boundary aligned smooth 3D cross-frame field》中使用球谐函数作为基函数,不同的是该文使用了不同的多项式<script type="math/tex">x^4+y^4+z^4</script>来在球上展开,并重新定义了目标函数,优化了求解,速度和质量都得到提升。</p><h3 id="一、-2D"><a href="#一、-2D" class="headerlink" title="一、 2D"></a>一、 2D</h3><h4 id="1-定义"><a href="#1-定义" class="headerlink" title="1.定义"></a>1.定义</h4><p>  该文采用基函数来表示标架:</p><script type="math/tex; mode=display">\begin{gather}let \quad \tilde{F}(\alpha)=cos(4\alpha)\quad \alpha \in[0,2\pi]\\\therefore F(\alpha)=\tilde{F}(\alpha-\theta)=cos(4\alpha-4\theta)=cos(4\theta)cos(4\alpha)+sin(4\theta)sin(4\alpha) \\let\quad B=(cos(4\alpha),sin(4\alpha)),a=(cos(4\theta),sin(4\theta))^{\top}\\\therefore F=Ba\end{gather}</script><p>即用<script type="math/tex">\tilde F</script>表示参考标架函数,<script type="math/tex">F</script>表示旋转<script type="math/tex">\theta</script>后的标架函数,下图是说明。</p><img src="/2020/09/14/2020-09-14-Practical-3D-Frame-Field-Generation-2D/image-20200914153648517.png" class=""><h4 id="2-能量函数"><a href="#2-能量函数" class="headerlink" title="2. 能量函数"></a>2. 能量函数</h4><p>  定义能量函数如下:</p><script type="math/tex; mode=display">\begin{aligned}E &=\sum_{i j} \int_{0}^{2 \pi}\left(F^{j}(\alpha)-F^{i}(\alpha)\right)^{2}d\alpha \\&=\sum_{i j} \int_{0}^{2 \pi}\left(B a^{j}-B a^{i}\right)^{2}d\alpha\\&=\sum_{i j}\left(a^{j}-a^{i}\right)^{\top}\left(\int_{0}^{2 \pi} B^{\top} B d \alpha\right)\left(a^{j}-a^{i}\right) \\&=\pi \sum_{i j}\left\|a^{j}-a^{i}\right\|^{2}\end{aligned}</script><p>ps:最后一步可以化简是因为<script type="math/tex">B</script>是正交的,且模长为<script type="math/tex">\sqrt{\pi}</script>。</p><h4 id="3-矩阵化"><a href="#3-矩阵化" class="headerlink" title="3. 矩阵化"></a>3. 矩阵化</h4><p>  定义边界或限制条件如下,</p><script type="math/tex; mode=display">a_{0}^{i}=\cos \left(4 \theta^{i}\right) ; \quad a_{1}^{i}=\sin \left(4 \theta^{i}\right)</script><p>即<script type="math/tex">\theta^i</script>是指定标架的方向,如为边界,即为法向方向。</p><p>  将<script type="math/tex">E</script>转换成<script type="math/tex">||AX-b||^2</script>形式,其中<script type="math/tex">X_{2i}=a_{0}^{i},X_{2i+1}=a_{1}^{i}</script>,所以<script type="math/tex">X</script>是<script type="math/tex">2n</script>维向量,<script type="math/tex">n</script>为顶点数,定义初始条件如下,</p><script type="math/tex; mode=display">\begin{aligned}\sqrt{\pi}\left(X_{2 i}-X_{2 j}\right) &=0 \\\sqrt{\pi}\left(X_{2 i+1}-X_{2 j+1}\right) &=0\end{aligned}</script><p>即相邻点相等。</p><p>  边界条件设置如下,</p><script type="math/tex; mode=display">\begin{aligned}C X_{2 i} &=C \cos 4 \theta^{i} \\C X_{2 i+1} &=C \sin 4 \theta^{i}\end{aligned}</script><p>其中<script type="math/tex">C=100</script>,为常数(经验得来)。</p><p>  其中<script type="math/tex">b=\mathbf{0}</script>,<script type="math/tex">2\mathcal{E}\times2n</script>矩阵<script type="math/tex">A</script>如下(<script type="math/tex">\mathcal{E}</script>为边),下标是行,上标是列</p><script type="math/tex; mode=display">A=\left\{\begin{aligned}&a_{\mathcal{E}_{ij}}^{2i}=a_{\mathcal{E}_{ij}}^{2i+1}=1,a_{\mathcal{E}_{ij}+1}^{2j}=a_{\mathcal{E}_{ij}+1}^{2j+1}=-1\ \ \ &&edge(v_i,v_j)\in \mathcal{E}\\&0 &&other\end{aligned}\right.</script><p>最小二乘方程的解等价于<script type="math/tex">A^{\top} AX=A^{\top}b</script></p><p>  解出该最小二乘方程后,可得到解并标准化:</p><script type="math/tex; mode=display">a^{i} \leftarrow\left(X_{2 i}, X_{2 i+1}\right)^{\top} /\left\|\left(X_{2 i}, X_{2 i+1}\right)\right\|</script>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>标架场</tag>
</tags>
</entry>
<entry>
<title>C++易踩Bug持续更新</title>
<link href="/2020/09/06/2020-08-12-C++%E6%98%93%E8%B8%A9Bug%E6%8C%81%E7%BB%AD%E6%9B%B4%E6%96%B0/"/>
<url>/2020/09/06/2020-08-12-C++%E6%98%93%E8%B8%A9Bug%E6%8C%81%E7%BB%AD%E6%9B%B4%E6%96%B0/</url>
<content type="html"><![CDATA[<h3 id="函数调用约定的常见问题"><a href="#函数调用约定的常见问题" class="headerlink" title="函数调用约定的常见问题"></a>函数调用约定的常见问题</h3><h4 id="1-参数传递顺序"><a href="#1-参数传递顺序" class="headerlink" title="1. 参数传递顺序"></a>1. 参数传递顺序</h4><p>  常用的有<code>__cdecl</code>:参数从右至左传递放在栈中,堆栈由调用者清除,支持可变长参数。(另外十分不建议在参数中包含了多个类似<code>a++</code>,<code>++a</code>,<code>a</code>,即同个参数的变换,十分容易引发问题)</p><p> 对于c/c++函数参数的读取顺序(默认<code>__cedel</code>),参数入栈时顺序的确是从右向左入栈,但是在入栈前会先把参数列表里的表达式从右向左算一遍得到表达式的结果,最后再把这些运算结果统一入栈。也就是说在参数入栈前,编译器会先把参数的表达式都处理掉,哪怕这些运算会改变其中某些参数的值;然后对于一般的操作来说,参数入栈时取值是直接从变量的内存地址里取的,但是对于a++操作,<strong>编译器会开辟一个缓冲区来保存当前a的值</strong>,然后再对a继续操作,<strong>最后参数入栈时的取值是从缓冲区取,而不是直接从a的内存地址里取</strong>。</p><p>因此,对于下面的程序:</p><pre><code class="hljs c++">a=<span class="hljs-number">10</span>;<span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d %d %d\n"</span>,a++,++a,a,a++)<span class="hljs-comment">//output:12 13 13 10</span></code></pre><p>  首先从右向左处理表达式,最后a的结果变成13.但是在处理a++的时候,从最右边开始,第一个a++放进缓冲区的值为10,第二个a++放进缓冲区的值为12.因此最后处理结果如上。注意++a和a的值都是直接从a的内存中取出来的,都是13.</p><img src="/2020/09/06/2020-08-12-C++%E6%98%93%E8%B8%A9Bug%E6%8C%81%E7%BB%AD%E6%9B%B4%E6%96%B0/image-20200812165825425.png" class="" title="image-20200812165825425"><p>  其中<code>__vectorcall</code>则用寄存器传浮点类型和向量(SIMD vector)类型的参数,可提高速度。</p><h4 id="2-链接库的函数调用约定和名字修饰规则不匹配引起堆栈异常"><a href="#2-链接库的函数调用约定和名字修饰规则不匹配引起堆栈异常" class="headerlink" title="2. 链接库的函数调用约定和名字修饰规则不匹配引起堆栈异常"></a>2. 链接库的函数调用约定和名字修饰规则不匹配引起堆栈异常</h4><p> ——<a href="https://blog.csdn.net/guoliang624/article/details/7648823?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param">参考文章链接</a></p><p>  函数调用时如果出现<strong>堆栈异常</strong>,可能由于函数调用约定不匹配引起的。比如动态链接库a有以下导出函数:</p><pre><code class="hljs C++"><span class="hljs-function"><span class="hljs-type">long</span> <span class="hljs-title">MakeFun</span><span class="hljs-params">(<span class="hljs-type">long</span> lFun)</span></span>;</code></pre><p>  动态库生成的时候采用的函数调用约定是<code>__stdcall</code>,所以编译生成的a.dll中函数<code>MakeFun</code>的调用约定是<code>_stdcall</code>,也就是函数调用时参数从右向左入栈,函数返回时自己还原堆栈。现在某个程序模块b要引用a中的<code>MakeFun</code>,b和a一样使用 C++方式编译,只是b模块的函数调用方式是<code>__cdecl</code>,由于b包含了a提供的头文件中<code>MakeFun</code>函数声明,所以<code>MakeFun</code>在b模块中被其它调用<code>MakeFun</code>的函数认为是<code>__cdecl</code>调用方式,b模块中的这些函数在调用完<code>MakeFun</code>要恢复堆栈,可是<code>MakeFun</code>已经在结束时自己恢复了堆栈,b模块中的函数这样多此一举就引起了栈指针错误,从而引发堆栈异常。</p><p>  宏观上的现象就是函数调用没有问题(因为参数传递顺序是一样的),<code>MakeFun</code>也完成了自己的功能,<strong>只是函数返回后引发错误</strong>。解决的方法也很简单,只要保证两个模块的在编译时设置相同的函数调用约定就行了。</p><p>  在了解函数调用约定和函数的修饰规则之后,再来看在C++程序中使用C语言编译的库时经常出现的LNK 2001错误。还以上面例子的两个模块为例,这一次两个模块在编译的时候都采用<code>__stdcall</code>调用约定,但是a.dll使用C语言的语法编译的(C语言方式),所以a.dll的载入库a.lib中<code>MakeFun</code>函数的名字修饰就是<code>_MakeFun@4</code>。b包含了a提供的头文件中<code>MakeFun</code>函数声明,但是由于b采用的是C++语言编译,所以M<code>akeFun</code>在b模块中被按照C++的名字修饰规则命名为<code>?MakeFun@@YGJJ@Z</code>,编译过程相安无事,链接程序时c++的链接器就到a.lib中去找<code>?MakeFun@@YGJJ@Z</code>,但是a.lib中只有<code>_MakeFun@4</code>,没有<code>?MakeFun@@YGJJ@Z</code>,于是链接器就报告:</p><pre><code class="hljs markdown">error LNK2001: unresolved external symbol ?MakeFun@@YGJJ@Z</code></pre><p>  解决的方法和简单,就是要让b模块知道这个函数是C语言编译的,<code>extern "C"</code>可以做到这一点。一个采用C语言编译的库应该考虑到使用这个库的程序可能是C++程序(使用C++编译器),所以在设计头文件时应该注意这一点。通常应该这样声明头文件:</p><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> _cplusplus</span><span class="hljs-keyword">extern</span> <span class="hljs-string">"C"</span> {<span class="hljs-meta">#<span class="hljs-keyword">endif</span></span> <span class="hljs-function"><span class="hljs-type">long</span> <span class="hljs-title">MakeFun</span><span class="hljs-params">(<span class="hljs-type">long</span> lFun)</span></span>; <span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> _cplusplus</span>}<span class="hljs-meta">#<span class="hljs-keyword">endif</span></span></code></pre><p> </p><h3 id="QT"><a href="#QT" class="headerlink" title="QT"></a>QT</h3><h4 id="1-QString与std-string的转换-防止乱码"><a href="#1-QString与std-string的转换-防止乱码" class="headerlink" title="1. QString与std::string的转换(防止乱码)"></a>1. QString与std::string的转换(防止乱码)</h4><p>  QString与std::string的转换在release模式下尤其容易造成乱码问题,使用如下方法可避免:</p><pre><code class="hljs c++">\\QString->std::stringQString qstr = <span class="hljs-built_in">QString</span>(<span class="hljs-string">"2|%1|%2"</span>).<span class="hljs-built_in">arg</span>(<span class="hljs-string">"3"</span>).<span class="hljs-built_in">arg</span>(<span class="hljs-string">"4"</span>);QByteArray qba = qstr.<span class="hljs-built_in">toLocal8Bit</span>();<span class="hljs-comment">//第一步</span>std::string std_strsendmid = qba.<span class="hljs-built_in">data</span>()<span class="hljs-comment">//第二步</span>\\std::string->QStringQString qstr = QString::<span class="hljs-built_in">fromLocal8Bit</span>(std_str);<span class="hljs-comment">//一步</span></code></pre><p>  PS:C++里另一种常见的字符问题是<code>std::string</code>的<code>c_str()</code>方法来转换<code>char*</code>。如果字符串是临时变量也容易被优化掉或者string改变后新得到的<code>char *</code>类型的字符串也会随之改变,因为<code>c_str()</code>返回的是string里封装好的<code>char *</code>指针(当前字符串的首字符的地址,虽然说返回的是<code>const char *</code>类型,但不要想当然的以为指向内容就不会改变了,因为我们只是无法通过它自身改变值,但是可以通过赋值给它的string改变它),因此最好使用<code>strcpy(str1,str2)</code>来拷贝一份。</p>]]></content>
<categories>
<category>bug</category>
</categories>
<tags>
<tag>C++</tag>
</tags>
</entry>
<entry>
<title>LoopyCuts-Computer Cutting Loops</title>
<link href="/2020/09/04/2020-09-04-LoopyCuts-Computer-Cutting-Loops/"/>
<url>/2020/09/04/2020-09-04-LoopyCuts-Computer-Cutting-Loops/</url>
<content type="html"><![CDATA[<h3 id="1-基本概念"><a href="#1-基本概念" class="headerlink" title="1.基本概念"></a>1.基本概念</h3><p>  feature line分为flat、concave、convex三种,由integrated curvature的<script type="math/tex">\theta</script>根据阈值<script type="math/tex">\bar \theta</script>判定:</p><script type="math/tex; mode=display">\left\{\begin{aligned}&\mathrm{flat},\ \ \ \ \ \ \ \ \ \ \ |\theta|<\bar\theta \\&\mathrm{concave},\ \ \ \ \theta<-\bar\theta\\&\mathrm{convex},\ \ \ \ \ \ \theta>\bar\theta\\\end{aligned}\right.</script><p>这里<script type="math/tex">\bar \theta=10^{\circ}</script>,例子如下图</p><p>  标架场采用《Mixed-integer quadrangulation》中的方法建立</p><img src="/2020/09/04/2020-09-04-LoopyCuts-Computer-Cutting-Loops/image-20200904213438466.png" class="" title="image-20200904213438466"><h3 id="2-Field-Coherent-Loops"><a href="#2-Field-Coherent-Loops" class="headerlink" title="2.Field-Coherent Loops"></a>2.Field-Coherent Loops</h3><p>  由标架场引导出field-coherent geodesic path:<script type="math/tex">\ell</script>切线与p点标架夹角小于<script type="math/tex">\frac{\pi}{4}</script>。为了描述其偏离程度使用以下公式</p><script type="math/tex; mode=display">\|w\|_{\mathrm{X}}=|w|\left(1+\alpha \frac{\angle\left(p_{\theta}, w\right)}{\pi / 4}\right)</script><p>其中<script type="math/tex">w</script>为切向量,<script type="math/tex">p_{\theta}</script>为标架之一,<script type="math/tex">\alpha</script>为惩罚系数。我们的目标是由点沿着field-coherent geodesic path返回点,即得到闭合曲线。而上述公式是为了避免造成过度偏离现象,如下图</p><img src="/2020/09/04/2020-09-04-LoopyCuts-Computer-Cutting-Loops/image-20200904213304148.png" class="" title="image-20200904213304148"><p>  定义曲线之间的距离如下:</p><script type="math/tex; mode=display">d\left(\ell_{i}, \ell_{j}\right)=\frac{1}{\left|\ell_{j}\right|} \int_{\ell_{j}} \operatorname{dist}\left(\ell_{i}, p_{\theta}\right) d p_{\theta}</script><p>其中<script type="math/tex">\operatorname{dist}\left(\ell_{i}, p_{\theta}\right)</script>为<script type="math/tex">p_{\theta}</script>与<script type="math/tex">\ell</script>之间的最短field-coherent geodesic path长度。</p><p>  设loop集合<script type="math/tex">\mathcal{L}=\{\ell_1 \dots \ell_k \}</script>,而<script type="math/tex">\ell \notin \mathcal{L}</script>, </p><script type="math/tex; mode=display">d(\mathcal{L}, \ell)=\frac{1}{|\ell|} \int_{\ell} \min _{\ell_{i} \in \mathcal{L}} \operatorname{dist}\left(\ell_{i}, p_{\theta}\right) d p_{\theta}</script><p>则离<script type="math/tex">\mathcal{L}</script>最远的<script type="math/tex">\bar \ell</script>为</p><script type="math/tex; mode=display">\bar{\ell}=\operatorname{argmax}_{\ell \in \mathrm{L}} d(\mathcal{L}, \ell)</script><img src="/2020/09/04/2020-09-04-LoopyCuts-Computer-Cutting-Loops/image-20200904221853685.png" class="" title="image-20200904221853685"><h3 id="3-特征loop"><a href="#3-特征loop" class="headerlink" title="3.特征loop"></a>3.特征loop</h3><p>  维护一个初始队列<script type="math/tex">Q</script>用来装loop,feature line <script type="math/tex">\ell</script> 分为两类处理,如下:</p><ol><li>闭合:如果为flat特征则加入<script type="math/tex">Q</script>队列;如果为concave,拷贝正反方向两份入队</li><li>非闭合:如果为flat拓展成一个loop入队,如果为concave,沿两端点分别生成两个loop入队</li></ol><h3 id="4-采样最远loop"><a href="#4-采样最远loop" class="headerlink" title="4.采样最远loop"></a>4.采样最远loop</h3><p>  每次采样最远的loop加入<script type="math/tex">Q</script>中,其终止条件需满足以下两个:</p><ol><li>Topological integrity :Q中每个loop至少与其他loop相交三次。</li><li>Geometric fit :path的面积与polyon的面积相差不超过阈值</li></ol><h3 id="5-维护采样池"><a href="#5-维护采样池" class="headerlink" title="5. 维护采样池"></a>5. 维护采样池</h3><p>  定义<script type="math/tex">\bar Q=Q \cup C\mathcal{F}</script>,其中<script type="math/tex">C\mathcal{F}</script>为convex feature,通过以下两种方式创建采样池<script type="math/tex">\mathcal{P}</script>:</p><ol><li>以固定间隔采样<script type="math/tex">\bar Q</script>中loop,对其中每个loop,跟踪一个垂直它自己新loop加入采样池<script type="math/tex">\mathcal{P}</script>,新loop需满足与<script type="math/tex">\bar Q</script>中loop不正切相交</li><li>在表面进行泊松分布采样(Poisson-Disk Sampling)获得点集<script type="math/tex">\mathrm{P}</script>,对于每个<script type="math/tex">p\in \mathrm{P}</script>跟踪两条新的loop,同样的他们需要满足与<script type="math/tex">\bar Q</script>中的不正切相交,加入采样池<script type="math/tex">\mathcal{P}</script></li></ol><p>每当从<script type="math/tex">\mathcal{P}</script>中选择一条最远loop加入<script type="math/tex">Q</script>中后,需要对采样池<script type="math/tex">\mathcal{P}</script>用方法1重新装填新的loop进去。</p><p>  为提高Topological integrity,从采样池中选取loop不再是简单的选择与<script type="math/tex">\bar Q</script>中最远的loop,而使用新的规则:设<script type="math/tex">\hat Q</script>为<script type="math/tex">Q</script>的子集且不满足Topological integrity条件,我们使采样池<script type="math/tex">\mathcal{P}</script>中与<script type="math/tex">\hat Q</script>中相交的loop具有更大的优先度被选择,即除了距离因素外加入相交次数作为优先度来快速提高拓扑完整性。根据以上规则在采样池中选择与<script type="math/tex">\bar Q</script>最远的loop<script type="math/tex">\ell</script> 加入<script type="math/tex">Q</script>,然后在采样池<script type="math/tex">\mathcal{P}</script>中移除所有与<script type="math/tex">\ell</script>正切相交的loop,并重新跟踪它们的源点<script type="math/tex">p \in \mathrm{P}</script>的两条正交loop(满足与新更新的<script type="math/tex">\bar Q</script>中的loop不正切相交)</p><img src="/2020/09/04/2020-09-04-LoopyCuts-Computer-Cutting-Loops/image-20200905013318085.png" class="" title="image-20200905013318085"><h3 id="6-输出"><a href="#6-输出" class="headerlink" title="6. 输出"></a>6. 输出</h3><p>  输出由两部分组成:队列<script type="math/tex">Q</script>与凸特征<script type="math/tex">C\mathcal{F}</script>。其中每个loop都以<script type="math/tex">((p_1,n_1)\dots(p_h,n_h))</script>形式给出,<script type="math/tex">p</script>为点,<script type="math/tex">n</script>为法向量。</p><p>  需要注意不是每个非闭合的feature都可以拓展成闭合loop,在拓展feature loop时也可能产生自相交的loop(这种类型舍弃),而那些无法闭合的feature我们把它们称为incomplete loop并加入<script type="math/tex">Q</script>,在下一个阶段进行处理。</p><h3 id="附1-Tracing-loops"><a href="#附1-Tracing-loops" class="headerlink" title="附1.Tracing loops"></a>附1.Tracing loops</h3><p>  trace loop使用《Tracing Field-Coherent Quad Layouts 》的方法在一个拓展出来的图<script type="math/tex">\mathcal{G}</script>上进行Dijkstra 算法,其中图<script type="math/tex">\mathcal{G}</script>由曲面M上的边和顶点,还有边上的采样得到斯坦纳点(Steiner point)和连接斯坦纳点和顶点的边组成。不同的是,这里的</p><p>  距离测量:对于集合<script type="math/tex">\mathcal{L}</script>中的每条loop,在每条loop上的每个点作为源点跑Dijkstra,从而找到最远的<script type="math/tex">\ell \notin \mathcal{L}</script>(这里很耗时)</p><p>  传播限制:在做Dijkstra时,搜索路径不能与<script type="math/tex">\bar Q</script>正切相交</p><h3 id="附2-相交说明"><a href="#附2-相交说明" class="headerlink" title="附2.相交说明"></a>附2.相交说明</h3><ol><li>正切相交判断:曲线相交,标架平行</li><li>垂直相交判断:曲线相交,标架相交</li></ol><img src="/2020/09/04/2020-09-04-LoopyCuts-Computer-Cutting-Loops/image-20200905005822283.png" class="" title="image-20200905005822283">]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>LoopyCuts</tag>
</tags>
</entry>
<entry>
<title>Instant Meshes 奇异点检测</title>
<link href="/2020/08/28/2020-02-22-Instant-Meshes-%E5%A5%87%E5%BC%82%E7%82%B9%E6%A3%80%E6%B5%8B/"/>
<url>/2020/08/28/2020-02-22-Instant-Meshes-%E5%A5%87%E5%BC%82%E7%82%B9%E6%A3%80%E6%B5%8B/</url>
<content type="html"><![CDATA[<p>  对于<script type="math/tex">4-Rosy\ field</script>,每个三角形网格的顶点<script type="math/tex">V_i</script>,都有一个标架<script type="math/tex">q_{si}</script>,根据以下式子,可判断奇异点:</p><script type="math/tex; mode=display">\begin{align}&\mathop{\arg\min}_{si,sj}\{q_{si}\cdot q_{sj}\}\ \ \ \ \ \ \ \ \ \ \ \ si,sj\in [0,3]\\& \mathrm{index}=(\sum(si-sj))\bmod 4\end{align}</script><script type="math/tex; mode=display">\left\{\begin{aligned}&\mathrm{is\ singularity},\ \ \ \ \ \ \mathrm{index}=1\ or\ 3\\&\mathrm{not\ singularity},\ \ \ \mathrm{else}\\\end{aligned}\right.</script><p>即在三角形中,相邻两个点的标架夹成的最小角的标架方向的序号的差的和与方向场的方向数的余数若为1或者3即该三角形内有奇异点。</p><img src="/2020/08/28/2020-02-22-Instant-Meshes-%E5%A5%87%E5%BC%82%E7%82%B9%E6%A3%80%E6%B5%8B/image-20200828191106279.png" class="" title="image-20200828191106279">]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>标架场</tag>
</tags>
</entry>
<entry>
<title>Instant Meshes 标架场方法</title>
<link href="/2020/08/22/2020-08-22-Instant-Meshes-%E6%A0%87%E6%9E%B6%E5%9C%BA%E6%96%B9%E6%B3%95/"/>
<url>/2020/08/22/2020-08-22-Instant-Meshes-%E6%A0%87%E6%9E%B6%E5%9C%BA%E6%96%B9%E6%B3%95/</url>
<content type="html"><![CDATA[<p>  在顶点<script type="math/tex">V_i</script>定义n-Rosy标架场,定义</p><script type="math/tex; mode=display">\begin{align}&\mathcal{R_{s_o}}(\mathbf{o},\mathbf{n},k):=rot(\mathbf{n},k\frac{2\pi}{s_o}),k \in \mathbb{Z} \\&\mathcal{R_{s_o}}(\mathbf{o},\mathbf{n}):=\{\mathcal{R_{s_o}}(\mathbf{o},\mathbf{n},0),\mathcal{R_{s_o}}(\mathbf{o},\mathbf{n},1),\dots,\mathcal{R_{s_o}}(\mathbf{o},\mathbf{n},s_o-1)\}\end{align}</script><p>第一个式子是将<script type="math/tex">\vec{o}</script>绕法向量<script type="math/tex">\vec{n}</script>旋转<script type="math/tex">k\frac{2\pi}{s_o}</script>角度,第二个式子即是在顶点处用<script type="math/tex">\vec{o}</script>对称旋转<script type="math/tex">n</script>次构成一个n-Rosy标架场</p><img src="/2020/08/22/2020-08-22-Instant-Meshes-%E6%A0%87%E6%9E%B6%E5%9C%BA%E6%96%B9%E6%B3%95/image-20200822165325984.png" class="" title="image-20200822165325984"><h3 id="1-Intrinsic-smoothness"><a href="#1-Intrinsic-smoothness" class="headerlink" title="1.Intrinsic smoothness"></a>1.Intrinsic smoothness</h3><p>  该方法为将顶点<script type="math/tex">V_i</script>的相邻顶点<script type="math/tex">V_j</script>旋转到<script type="math/tex">V_i</script>的切平面上,然后寻找<script type="math/tex">\vec{o_i}</script>与<script type="math/tex">\vec{o_j}</script>的最小旋转角度,并用高斯赛德尔方法进行迭代求解。</p><p>  定义能量函数:</p><script type="math/tex; mode=display">E(O, \mathbf{k}):=\sum_{i \in \mathcal{V}} \sum_{j \in \mathcal{N}(i)} \angle\left(\mathbf{o}_{i}, \mathcal{R}_{s_{o}}\left(\mathbf{o}_{j i}, \mathbf{n}_{i}, k_{i j}\right)\right)^{2}</script><p>其中<script type="math/tex">\mathbf{o}_{j i}:=\operatorname{rot}\left(\mathbf{n}_{j} \times \mathbf{n}_{i}, \angle\left(\mathbf{n}_{j}, \mathbf{n}_{i}\right)\right) \mathbf{o}_{j}</script>,即<script type="math/tex">o_j</script>旋转到<script type="math/tex">V_i</script>切平面上后的向量。<script type="math/tex">k_{ij}</script>为旋转次数,要求解该能量函数,可通过以下方法</p><script type="math/tex; mode=display">\begin{align}&k_{i j}:=\underset{0 \leq k<s_{o}}{\arg \min } \angle\left(\mathbf{o}_{i}, \mathcal{R}_{s_{o}}\left(\mathbf{o}_{j i}, \mathbf{n}_{i}, k\right)\right)\\&\mathbf{o}_{i} \leftarrow \sum_{j \in \mathcal{N}(i)} w_{i j} \mathcal{R}_{s_{o}}\left(\mathbf{o}_{j i}, \mathbf{n}_{i}, k_{i j}\right), \quad \mathbf{o}_{i} \leftarrow \mathbf{o}_{i} /\left\|\mathbf{o}_{i}\right\|\end{align}</script><p>即先寻找最佳旋转次数,然后通过拉普拉斯权重归一化,由于搜寻空间是很小的(<script type="math/tex">s_o</script>,如果是n-Rosy,也就是n,而n通常是很小的一个常数),可用暴力方法,若顶点<script type="math/tex">V_i</script>周围有<script type="math/tex">m</script>个点,那迭代一次的搜索空间大小也只有<script type="math/tex">mn</script></p><p>然后通过高斯赛德尔迭代:</p><script type="math/tex; mode=display">\begin{array}{ll}\mathbf{o}_{i}^{\prime} \leftarrow w_{i j_{1}} \mathcal{R}_{s_{o}}\left(\mathbf{o}_{j_{1} i}, \mathbf{n}_{i}, k_{i j_{1}}\right), & \mathbf{o}_{i} \leftarrow \mathbf{o}_{i}^{\prime} /\left\|\mathbf{o}_{i}^{\prime}\right\| \\\mathbf{o}_{i}^{\prime} \leftarrow \mathbf{o}_{i}^{\prime}+w_{i j_{2}} \mathcal{R}_{s_{o}}\left(\mathbf{o}_{j_{2} i}, \mathbf{n}_{i}, k_{i j_{2}}\right), & \mathbf{o}_{i} \leftarrow \mathbf{o}_{i}^{\prime} /\left\|\mathbf{o}_{i}^{\prime}\right\|\end{array}</script><h3 id="2-Extrinsic-smoothness"><a href="#2-Extrinsic-smoothness" class="headerlink" title="2.Extrinsic smoothness"></a>2.Extrinsic smoothness</h3><p>  该方法区别于上面的方法为直接在三维空间里求解能量函数,即不再旋转到切平面,则能量函数改为:</p><script type="math/tex; mode=display">E_{\mathrm{e}}(O, k):=\sum_{i \in \mathcal{V}} \sum_{j \in \mathcal{N}(i)} \angle\left(\mathcal{R}_{s_{o}}\left(\mathbf{o}_{i}, \mathbf{n}_{i}, k_{i j}\right), \mathcal{R}_{s_{o}}\left(\mathbf{o}_{j}, \mathbf{n}_{j}, k_{j i}\right)\right)^{2}</script><p>则<script type="math/tex">k_{ij}</script>的最优解为</p><script type="math/tex; mode=display">\left(k_{i j}, k_{j i}\right):=\underset{0 \leq k, l<s_{o}}{\arg \min } \angle\left(\mathcal{R}_{s_{o}}\left(\mathbf{o}_{i}, \mathbf{n}_{i}, k\right), \mathcal{R}_{s_{o}}\left(\mathbf{o}_{j}, \mathbf{n}_{j}, l\right)\right)^{2}</script><p>可以看到搜索空间变为<script type="math/tex">n^2</script>,其实它还是很小的,也是可以暴力求的。下一步则依然还是高斯赛德尔迭代。</p><h3 id="3-说明"><a href="#3-说明" class="headerlink" title="3.说明"></a>3.说明</h3><ol><li>上述算法有解的条件是收敛,而事实就是它就是<strong>收敛</strong>的。</li><li>上述算法因为它是基于局部的,所以可以<strong>支持并行</strong>。</li><li>初始标架场被设置为随机的切向量,锐利特征则通过二面角阈值来判断,锐利边上顶点的法向量为任一面的法向量,而其余的可用角度权重等来定义。</li><li>Gauss–Seidel method可能陷入局部最小解,可通过随机法避免,而该文提出了另一种<strong>多分辨率层次法(Multiresolution hierarchy)</strong>来避免这种情况,如下。 <img src="/2020/08/22/2020-08-22-Instant-Meshes-%E6%A0%87%E6%9E%B6%E5%9C%BA%E6%96%B9%E6%B3%95/image-20200822165143871.png" class="" title="image-20200822165143871"></li></ol>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>标架场</tag>
</tags>
</entry>
<entry>
<title>LoopyCuts编译运行方法</title>
<link href="/2020/08/16/LoopyCuts%E7%BC%96%E8%AF%91%E8%BF%90%E8%A1%8C%E6%96%B9%E6%B3%95/"/>
<url>/2020/08/16/LoopyCuts%E7%BC%96%E8%AF%91%E8%BF%90%E8%A1%8C%E6%96%B9%E6%B3%95/</url>
<content type="html"><![CDATA[<p>这绝对是目前我编译过的最心酸的代码:),第一部分的pro配置和部分源码头文件是有误的,第二部分的pro、源码有误,更是几乎翻遍了依赖库cinolib的issue和commit:(</p><h4 id="一-loop-distributor部分"><a href="#一-loop-distributor部分" class="headerlink" title="一.loop_distributor部分"></a>一.loop_distributor部分</h4><h5 id="1-windos-qtcreater下:"><a href="#1-windos-qtcreater下:" class="headerlink" title="1. windos qtcreater下:"></a>1. windos qtcreater下:</h5><p>如果使用windows且使用64位,按以下步骤:</p><ol><li><p>glwidget.h文件中在<code>#define GLWIDGET_H</code>后面添加以下代码(一定得按此顺序添加头文件)</p><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">define</span> GLEW_STATIC</span><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><GL/glew.h></span></span></code></pre></li><li><p>将编译好的一份glew库放在loop_distributor文件夹下,同时我在里面lib下的glew库的64位lib库里添加了一份win下的<code>OpenGL32</code>和<code>GlU32</code>静态库</p></li><li><p>修改loop_distributor.pro文件,将以下修改</p><pre><code class="hljs cmake">win32{ DEFINES += NOMINMAX <span class="hljs-comment">#LIBS +=$$ANTDIR/lib/AntTweakBar.lib</span> LIBS +=-L$$ANTDIR/lib/ -lAntTweakBar64<span class="hljs-comment">#需添加64位,并用该写法否则无法链接dll</span>}</code></pre><p>同时在最后添加glew库的依赖</p><pre><code class="hljs cmake"><span class="hljs-comment">#windos下还需以下依赖,同时取消glwidget.h 中#include <GL/glew.h>的注释,并在其前面添加#define GLEW_STATIC</span>win32: LIBS += -L$$PWD/../lib/glew-<span class="hljs-number">2.1</span>.<span class="hljs-number">0</span>/lib/Release/x64/ -lglew32sINCLUDEPATH += $$PWD/../lib/glew-<span class="hljs-number">2.1</span>.<span class="hljs-number">0</span>/<span class="hljs-keyword">include</span>DEPENDPATH += $$PWD/../lib/glew-<span class="hljs-number">2.1</span>.<span class="hljs-number">0</span>/<span class="hljs-keyword">include</span>win32: LIBS += -lOpenGL32win32: LIBS += -lGlU32win32:QMAKE_CXXFLAGS += /bigobj<span class="hljs-comment">#debug模式下编译需要加该项</span></code></pre><p>编译完成运行时选择<code>test_data</code>文件夹下将里面的<code>.sharp</code>和<code>.rosy</code>改成和obj模型同名文件,同时将该模型路径以参数方式运行程序(项目->run->command line arguments),否则无法运行。</p><p>这样调试还不行,而且qtcreater调试体验也不好,不过按照以上方法设置后会为vs中的设置大大减轻压力,因为vs能识别一部分pro文件。</p></li></ol><h5 id="2-vs"><a href="#2-vs" class="headerlink" title="2.vs"></a>2.vs</h5><p>  导入pro文件后运行,debug模式下提示缺少<code>AntTweakBar64.dll</code>,在调试->属性->配置属性->调试->环境中添加以下路径<code>PATH=%PATH%;G:\test\LoopyCuts\lib\AntTweakBar1.16\lib;E:\QT\5.12.9\msvc2015_64\bin</code></p><p>第二个是缺少dll的路径,第三个是qt中的dll路径(不添加会继续提示缺少)。缺少dll的解决方法也可以用其他的,我一直用这个,比其他的体验真的好。。</p><p>  release下上述方法失效,解决方法就是拷贝缺少的dll到包含有exe的同文件夹下。</p><p>  vs中添加命令行参数的方法为调试->属性->配置属性->调试->命令参数中设置</p><h4 id="二-volumetric-cutter部分"><a href="#二-volumetric-cutter部分" class="headerlink" title="二.volumetric_cutter部分"></a>二.volumetric_cutter部分</h4><h5 id="1-tetgen"><a href="#1-tetgen" class="headerlink" title="1. tetgen"></a>1. tetgen</h5><p>  正常cmake,记得用msvc编译,选择x64,不然默认出linux下的.a静态库</p><h5 id="2-程序部分"><a href="#2-程序部分" class="headerlink" title="2.程序部分"></a>2.程序部分</h5><p>  提前说明以下,这个程序依赖的一个库cinolib无法用msvc编译,巨坑,<a href="https://github.com/mlivesu/cinolib/issues/12">issue链接</a>!首先在pro文件中配置好tetgen的依赖,然后如果有以下报错依次解决:</p><ol><li><p><code>octree.cpp</code>661行报错,改成以下代码,<a href="https://github.com/mlivesu/cinolib/commit/4849cb3beeb2ea1a26526bc6f7fc265da4ed2c4f">相关commit链接</a>:</p><pre><code class="hljs c++"><span class="hljs-function">AABB <span class="hljs-title">s_box</span><span class="hljs-params">(s[<span class="hljs-number">0</span>],s[<span class="hljs-number">1</span>])</span></span>;</code></pre></li><li><p><code>hexmesh.cpp</code>405行和415行报错,<a href="https://github.com/mlivesu/cinolib/commit/df7cf8f4dfa6dbbebf1e6b04c26262185861335d">相关commit链接</a>,依次修改为</p><pre><code class="hljs c++"><span class="hljs-keyword">this</span>-><span class="hljs-built_in">update_p_quality</span>(pid);<span class="hljs-comment">//405</span><span class="hljs-keyword">this</span>-><span class="hljs-built_in">update_quality</span>();<span class="hljs-comment">//415</span></code></pre></li><li><p><code>draw_lines_tris.cpp</code>160行<code>glGenerateMimap(GL_TEXTURE_2D)</code>报错,未找到相关解决方法,注释掉后能运行,有个为了兼容msvc的<a href="https://github.com/mlivesu/cinolib/commit/8a41a44642d75cc1725157effef222b8c27488a4">commit</a>也是注释掉该方法,可是在后续commit又取消了该注释,<a href="https://github.com/mlivesu/cinolib/commit/2d9f95ccc8f63a0bc0a0a17989cdfec5649e41c0#diff-3fd1c919d023c8ba52b7932fc71fa5c9">链接</a>,具体效果未知。ps:在linux中不注释该方法也无法运行,当前版本都是高于这两个commit的按理说不应该有这个问题。</p></li><li><p>以上步骤能在linux下编译,如果使用windows,还需以下步骤:</p><ul><li><p>在lib\cinolib\include\cinolib\gl文件夹下的draw_xxx.h中头文件中的win下的条件编译宏改成以下代码</p><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><windows.h></span></span><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><GL/gl.h></span></span><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><GL/glu.h></span></span></code></pre></li><li><p>如果在win下使用mingw编译的话不仅速度慢而且因为obj文件太大无法编译,<strong>不推荐</strong></p></li><li><p>使用msvc编译,msvc不支持变长数组,但源码中有些地方使用变长数组,所以在源码中做如下修改,使用动态申请,但勿忘delete</p><p>laplacian.cpp54行: </p><p>  <code>uint base[n]</code>改用<code>uint *base=new uint[n]()</code>,勿忘delete!</p><p>ambient_occlusion.cpp195行:</p><p>  <code>float depth_buffer[buffer_size*buffer_size]</code>改为<code>float *depth_buffer=new float[buffer_size*buffer_size]();</code>勿忘delete!</p></li></ul></li><li><p>关于这个的pro改动地方有点多,未作记录,就不列出</p></li></ol><h4 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h4><ol><li>第一部分在<strong>windos下编译运行成功</strong>,linux下的话应该可以添加glew库运行,我ubuntu机子上没有该库就没尝试。</li><li>第二部分第三个错误未完美解决,翻到两个相关commit(前一个注释,后一个取消注释??),注释掉后在<strong>linux编译成功,windos下修改后能在msvc编译下编译成功</strong>,mingw编译特别慢,而且还出错。</li><li>以上两部分使用方法和运行效果还未测试。</li></ol>]]></content>
<categories>
<category>教程</category>
</categories>
<tags>
<tag>教程</tag>
</tags>
</entry>
<entry>
<title>Gauss、Stokes、Green公式</title>
<link href="/2020/08/13/2020-02-22-Gauss%E3%80%81Stokes%E3%80%81Green%E5%85%AC%E5%BC%8F/"/>
<url>/2020/08/13/2020-02-22-Gauss%E3%80%81Stokes%E3%80%81Green%E5%85%AC%E5%BC%8F/</url>
<content type="html"><![CDATA[<p>  以下<script type="math/tex">F</script>为向量场,<script type="math/tex">\mathbf{n}</script>为法向量,<script type="math/tex">\mathbf{\tau}</script>为切向量。</p><h4 id="1-Gauss"><a href="#1-Gauss" class="headerlink" title="1. Gauss"></a>1. Gauss</h4><script type="math/tex; mode=display">\int_{V}\nabla\cdot Fdv=\int_{S}F\cdot d\mathbf{A}=\int_{S}F\cdot \mathbf{n}dA</script><h4 id="2-Stokes"><a href="#2-Stokes" class="headerlink" title="2. Stokes"></a>2. Stokes</h4><script type="math/tex; mode=display">\int_{S}\nabla\times F\cdot d\mathbf{A}=\int_{S}\nabla\times F\cdot \mathbf{n}dA=\oint_{\partial S}F\cdot d\mathbf{l}=\oint_{\partial S}F\cdot \mathbf{\tau}dl</script><h4 id="3-Green"><a href="#3-Green" class="headerlink" title="3. Green"></a>3. Green</h4><h5 id="3-1-散度形式"><a href="#3-1-散度形式" class="headerlink" title="3.1 散度形式"></a>3.1 散度形式</h5><script type="math/tex; mode=display">\int_{S}\nabla\cdot F dA=\oint_{\partial S}F\cdot d\mathbf{l}=\int_{\partial S}F\cdot\mathbf{n}dl</script><h5 id="3-2-旋度形式"><a href="#3-2-旋度形式" class="headerlink" title="3.2 旋度形式"></a>3.2 旋度形式</h5><script type="math/tex; mode=display">\int_{S}\nabla\times F\cdot d\mathbf{A}=\oint_{\partial S}F\cdot d\mathbf{l}=\oint_{\partial S}F\cdot \mathbf{\tau}dl</script><h4 id="4-说明"><a href="#4-说明" class="headerlink" title="4. 说明"></a>4. 说明</h4><p>  可理解为内部区域的性质通过边界来反映,即:</p><ol><li>Gauss:内部体积元的散度积分等于表面通量积分</li><li>Stokes:表面面积元矢量的旋度积分等于边界的环量积分</li><li>Green:两个形式分别为以上的退化形式</li></ol>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>math</tag>
</tags>
</entry>
<entry>
<title>空间曲线Frenet标架</title>
<link href="/2020/08/10/2020-08-10-%E7%A9%BA%E9%97%B4%E6%9B%B2%E7%BA%BFFrenet%E6%A0%87%E6%9E%B6/"/>
<url>/2020/08/10/2020-08-10-%E7%A9%BA%E9%97%B4%E6%9B%B2%E7%BA%BFFrenet%E6%A0%87%E6%9E%B6/</url>
<content type="html"><![CDATA[<img src="/2020/08/10/2020-08-10-%E7%A9%BA%E9%97%B4%E6%9B%B2%E7%BA%BFFrenet%E6%A0%87%E6%9E%B6/%E7%A9%BA%E9%97%B4%E6%9B%B2%E7%BA%BFFrenet%E6%A0%87%E6%9E%B6.jpg" class=""><p><script type="math/tex">k(s)</script>为曲率,而<script type="math/tex">\tau(s)</script>为挠率,用来描述扭转程度的,也可以理解为空间曲线离开密切平面的程度。</p>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>math</tag>
</tags>
</entry>
<entry>
<title>Directional field synthesis, design, and processing</title>
<link href="/2020/08/08/2020-08-08-Directional-field-synthesis,-design,-and-processing/"/>
<url>/2020/08/08/2020-08-08-Directional-field-synthesis,-design,-and-processing/</url>
<content type="html"><![CDATA[<h3 id="1-Types-of-Directional-Fields"><a href="#1-Types-of-Directional-Fields" class="headerlink" title="1. Types of Directional Fields"></a>1. Types of Directional Fields</h3><ol><li>方向场:每个点上的方向或矢量组成的一个大小为<script type="math/tex">N</script>的集合。</li><li><em>RoSy fields</em>: <em>rotationally-symmetric direction fields</em>,对称角度为<script type="math/tex">\frac{2\pi}{N}</script></li></ol><img src="/2020/08/08/2020-08-08-Directional-field-synthesis,-design,-and-processing/image-20200808150152601.png" class="" title="image-20200808150152601"><p>以上右上角带小标的代表模大小相同。</p><h3 id="2-Differential-Geometry-of-Directional-Fields"><a href="#2-Differential-Geometry-of-Directional-Fields" class="headerlink" title="2. Differential Geometry of Directional Fields"></a>2. Differential Geometry of Directional Fields</h3><h4 id="2-1-Differential-and-Riemannian-Structure"><a href="#2-1-Differential-and-Riemannian-Structure" class="headerlink" title="2.1 Differential and Riemannian Structure"></a>2.1 Differential and Riemannian Structure</h4><ol><li><p>Tangent Bundle and Vector Fields:切丛和向量场,由曲面M的切向量组成。</p></li><li><p>Cotangent Bundle and 1-forms ×</p></li><li><p>Connections and Parallel Transport ×</p></li><li><p>Riemannian Metric ×</p></li><li><p>Levi-Civita Connection ×</p></li><li><p>Holonomy ×</p></li></ol><h4 id="2-2-Vector-Field-Topology"><a href="#2-2-Vector-Field-Topology" class="headerlink" title="2.2 Vector Field Topology"></a>2.2 Vector Field Topology</h4><p>  奇异点的指标,是包围奇异点的封闭曲线上的向量逆时针遍历一圈后所旋转的次数。而<em>Poincare–Hopf theorem</em>指出在向量场上所有奇异点指标之和<script type="math/tex">\sum index =2-2g</script>,其中<script type="math/tex">g</script>为规格</p><h3 id="2-3-Vector-Calculus"><a href="#2-3-Vector-Calculus" class="headerlink" title="2.3 Vector Calculus"></a>2.3 Vector Calculus</h3><ol><li>梯度、散度、旋度的介绍。</li><li>霍奇分解和协变场 ×</li><li>外积分</li></ol><p> </p>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>图形学</tag>
</tags>
</entry>
<entry>
<title>记一个QT中QOpenGLWidget 与 QGLWidget的坑</title>
<link href="/2020/08/07/2020-08-07-%E8%AE%B0%E4%B8%80%E4%B8%AAQT%E4%B8%ADQOpenGLWidget-%E4%B8%8E-QGLWidget%E7%9A%84%E5%9D%91/"/>
<url>/2020/08/07/2020-08-07-%E8%AE%B0%E4%B8%80%E4%B8%AAQT%E4%B8%ADQOpenGLWidget-%E4%B8%8E-QGLWidget%E7%9A%84%E5%9D%91/</url>
<content type="html"><![CDATA[<p>  因为QGLWidget在文档中显示过时,就采用了QOpenGLWidget,因为要用到多重采样来抗锯齿</p><pre><code class="hljs c++">QSurfaceFormat format;format.<span class="hljs-built_in">setSamples</span>(<span class="hljs-number">4</span>);QSurfaceFormat::<span class="hljs-built_in">setDefaultFormat</span>(format);</code></pre><p>结果造成像素深度<code>winz</code>无法读取</p><pre><code class="hljs c++"><span class="hljs-built_in">glReadPixels</span>((<span class="hljs-type">int</span>)winx, (<span class="hljs-type">int</span>)winy, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, GL_DEPTH_COMPONENT, GL_FLOAT,&winz);</code></pre><p><code>winz</code>一直不会改变。</p><p>  这个bug找了两天多,最终才确定是QOpenGLWidget的多重采样造成的。在QT文档中有</p><pre><code class="hljs c++">QOpenGLWidget always renders offscreen, <span class="hljs-keyword">using</span> framebuffer objects. QGLWidget on the other hand uses a native window <span class="hljs-keyword">and</span> surface.</code></pre><p>同时关于Multisampling的记录是</p><pre><code class="hljs C++">MultisamplingTo enable multisampling, <span class="hljs-function">set the number of requested samples on the QSurfaceFormat that is passed to <span class="hljs-title">setFormat</span><span class="hljs-params">()</span>. On systems that <span class="hljs-keyword">do</span> <span class="hljs-keyword">not</span> support it the request may get ignored.</span><span class="hljs-function">Multisampling support <span class="hljs-keyword">requires</span> support <span class="hljs-keyword">for</span> multisampled renderbuffers <span class="hljs-keyword">and</span> framebuffer blits. On OpenGL ES 2.0 implementations it is likely that these will <span class="hljs-keyword">not</span> be present. This means that multisampling will <span class="hljs-keyword">not</span> be available. With modern OpenGL versions <span class="hljs-keyword">and</span> OpenGL ES 3.0 <span class="hljs-keyword">and</span> up <span class="hljs-keyword">this</span> is usually <span class="hljs-keyword">not</span> a problem anymore. </span></code></pre><p>  可以看到它是<strong>离屏渲染(offscreen)</strong>的,要求使用<code>multisampled renderbuffers</code>和<code>framebuffer</code>,这是一个很大的不同,所以如果坚持要使用它并且要用到读取深度之类的可行思路是重新绑定FBO后去读取。而在QGLWidget中只需要在构造函数的初始化列表中用</p><pre><code class="hljs c++">:<span class="hljs-built_in">QGLWidget</span>(<span class="hljs-built_in">QGLFormat</span>(QGL::SampleBuffers | QGL::AlphaChannel), parent)</code></pre>]]></content>
<categories>
<category>bug</category>
</categories>
<tags>
<tag>bug</tag>
</tags>
</entry>
<entry>
<title>Mathematics for 3D Game Programming and Computer Graphics 1-4章笔记</title>
<link href="/2020/07/19/Mathematics-for-3D-Game-Programming-and-Computer-Graphics/"/>
<url>/2020/07/19/Mathematics-for-3D-Game-Programming-and-Computer-Graphics/</url>
<content type="html"><![CDATA[<h1 id="3D游戏与计算机图形学中的数学方法"><a href="#3D游戏与计算机图形学中的数学方法" class="headerlink" title="3D游戏与计算机图形学中的数学方法"></a>3D游戏与计算机图形学中的数学方法</h1><h2 id="第一章-渲染管线"><a href="#第一章-渲染管线" class="headerlink" title="第一章 渲染管线"></a>第一章 渲染管线</h2><h3 id="1-1-图形处理器"><a href="#1-1-图形处理器" class="headerlink" title="1.1 图形处理器"></a>1.1 图形处理器</h3><p>CPU-GPU通信:</p><p> 内存<——>CPU——>应用程序——>渲染库(如OpenGL)——>图形卡驱动——>GPU<——>显存</p><p>显存主要缓冲区:</p><ol><li>前后图像缓冲区切换,前:用户可见;后:用户不可见。切换要与屏幕刷新率同步,否则会画面撕裂,即不同时刻的画面出现在屏幕上,屏幕不同部分的画面来自不同缓冲区。</li><li>深度缓冲区(z-缓冲区):每个像素在图像中的深度值,即该像素里虚拟相机距离。因此可用于隐藏面剔除(只允许显示深度值小于图像缓冲区中像素的深度值的像素)。</li></ol><h3 id="1-2-顶点变换"><a href="#1-2-顶点变换" class="headerlink" title="1.2 顶点变换"></a>1.2 顶点变换</h3><p> 为了将三维数据变换为二维的图像显示在屏幕上(这是目的,真正进行这步操作的是光栅化),在模型坐标在世界坐标中映射入相机视界,再通过投影变换来实现近大远小,通过齐次裁剪保证只渲染视界以内元素。注意,顶点也是携带纹理坐标的,这样可以通过纹理映射找到颜色值。</p><p> 个人理解为坐标变换。</p><h3 id="1-3-光栅化和段操作"><a href="#1-3-光栅化和段操作" class="headerlink" title="1.3 光栅化和段操作"></a>1.3 光栅化和段操作</h3><p> 将几何元素经过变换填充至屏幕的过程称为光栅化。</p><p> 光栅化后,GPU计算出每个像素的深度、各顶点插值的颜色、差值得出的纹理坐标与像素自己的位置合称为段数据。段数据经过段操作处理后便可写入图像缓冲区,以下是操作的过程</p><pre><code class="hljs flow">st=>start: 段数据op2=>operation: 像素包含测试op3=>operation: 裁剪测试op4=>operation: Alpha测试op5=>operation: 模板测试op6=>operation: 深度测试op7=>operation: 混合ed=>end: 图像缓冲区st(right)->op2(right)->op3(right)->op4(right)->op5(right)->op6(right)->op7(right)->ed</code></pre><h3 id="1-4-总结"><a href="#1-4-总结" class="headerlink" title="1.4 总结"></a>1.4 总结</h3><p> 这章主要介绍了3维物体在2维屏幕上展现的基本过程,主要是概念上的介绍。</p><pre><code class="hljs flow">st=>start: 三维数据op1=>operation: 坐标变换op2=>operation: 颜色计算op3=>operation: 光栅化ed=>end: 二维屏幕st(right)->op1(right)->op2(right)->op3(right)->ed</code></pre><h2 id="第二章-向量"><a href="#第二章-向量" class="headerlink" title="第二章 向量"></a>第二章 向量</h2><p> 列向量$P$在$Q$上的投影$proj_QP$通过矩阵可表示为:</p><script type="math/tex; mode=display">\begin{align} proj_QP &=\frac{Q^TP}{||Q||}\\ &=Q\frac{Q^TP}{||Q||^2}\\ &=\frac{QQ^T}{Q^TQ}P\\\end{align}</script><p>,其中$\frac{QQ^T}{Q^TQ}$为投影矩阵。 </p><p> 施密特正交化,<strong>将集合中的每个向量减去该向量在其前面的所有向量上的投影</strong>,则处理完后所有向量正交。</p><img src="/2020/07/19/Mathematics-for-3D-Game-Programming-and-Computer-Graphics/image-20200801230112225.png" class="" title="image-20200801230112225"><h2 id="第三章-矩阵"><a href="#第三章-矩阵" class="headerlink" title="第三章 矩阵"></a>第三章 矩阵</h2><p> 逆矩阵,行列式,伴随矩阵,线性方程组,克莱姆法则,特征向量,实对称矩阵,矩阵对角化。</p><h2 id="第四章-坐标变换"><a href="#第四章-坐标变换" class="headerlink" title="第四章 坐标变换"></a>第四章 坐标变换</h2><h3 id="4-1-线性变换"><a href="#4-1-线性变换" class="headerlink" title="4.1 线性变换"></a>4.1 线性变换</h3><p>  设在3D坐标系$C_1$中$P$点坐标为$(x,y,z)$,将其转换到坐标系$C_2$,则$(x^{‘},y^{‘},z^{‘})$为:</p><script type="math/tex; mode=display">\begin{bmatrix} x^{'}\\y^{'}\\z^{'}\end{bmatrix} =\begin{bmatrix} U_1&V_1&W_1\\U_2&V_2&W_2\\U_3&V_3&W_3\end{bmatrix} \begin{bmatrix} x\\y\\z\end{bmatrix}+ \begin{bmatrix} T_1\\T_2\\T_3\end{bmatrix}</script><p>其中$U,V,W$组成的矩阵表示坐标轴变换的方式,$T$向量为$C_1$坐标系到$C_2$坐标系平移向量。</p><p> 若变换矩阵为<strong>正交矩阵</strong>,则该变换可保持向量的长度和角度不变(向量的模不变确保物体的大小不变,向量的点乘不变确保物体的角度不变),因此正交矩阵只可用于旋转变换和反射变换或者是二者的组合。</p><p> 旋转变换不改变手性,反射变换改变手性。因此,奇数次反射变换改变<strong>原坐标系</strong>手性,偶数次反射变换不改变<strong>原坐标系</strong>手性,且等价于旋转变换,故任意多的反射变换可分解为一次旋转变换加<strong>最多一次</strong>反射变换。若$M$为变换矩阵,$detM<0$说明改变了手性,$detM>0$说明为改变手性。</p><p> 若$M$为正交阵,应用上述规律,由$detM=\pm1$,当$detM=1$,说明为一个旋转变换;$detM=-1$说明为一个旋转变换和一个反射变换的组合变换。</p><h3 id="4-2-比例变换"><a href="#4-2-比例变换" class="headerlink" title="4.2 比例变换"></a>4.2 比例变换</h3><p> 通过左乘对角阵来进行$x,y,z$轴的比例变换,如果要沿任意轴变换,可先进行坐标系变换,再比例变换,再还原坐标系,如下。</p><script type="math/tex; mode=display">\begin{bmatrix} x^{'}\\y^{'}\\z^{'}\end{bmatrix} =\begin{bmatrix} U_1&V_1&W_1\\U_2&V_2&W_2\\U_3&V_3&W_3\end{bmatrix}\begin{bmatrix} a&0&0\\0&b&0\\0&0&c\end{bmatrix}\begin{bmatrix} U_1&V_1&W_1\\U_2&V_2&W_2\\U_3&V_3&W_3\end{bmatrix}^{-1}\begin{bmatrix} x\\y\\z\end{bmatrix}</script><h3 id="4-3-旋转变换"><a href="#4-3-旋转变换" class="headerlink" title="4.3 旋转变换"></a>4.3 旋转变换</h3><h4 id="4-3-1-二维"><a href="#4-3-1-二维" class="headerlink" title="4.3.1 二维"></a>4.3.1 二维</h4><p> 如果要求$P$逆时针旋转$\theta$得到的$P^{‘}$,记$Q$为$P$逆时针旋转90°后的向量,即$(-P_y,P_x)$,则$P$与$Q$正好组成了<strong>该平面内的一组正交向量</strong>,任何向量都可由其线性表出,由基本几何和三角学可得到:</p><script type="math/tex; mode=display">P^{'}=Pcos\theta+Qsin\theta</script><p>则</p><script type="math/tex; mode=display">\begin{aligned}P_x^{'}&=P_xcos\theta-P_ysin\theta\\P_{y}^{'}&=P_ysin\theta-P_xcos\theta\\\end{aligned}</script><p>写成矩阵为</p><script type="math/tex; mode=display">P^{'}=\begin{bmatrix}cos\theta&-sin\theta\\sin\theta&cos\theta\end{bmatrix}P</script><h4 id="4-3-2-三维绕坐标轴旋转"><a href="#4-3-2-三维绕坐标轴旋转" class="headerlink" title="4.3.2 三维绕坐标轴旋转"></a>4.3.2 三维绕坐标轴旋转</h4><img src="/2020/07/19/Mathematics-for-3D-Game-Programming-and-Computer-Graphics/image-20200606132127655.png" class="" title="image-20200606132127655"><p> 注意$R_y(\theta)$与其他不同是因为如果按照逆时针旋转,由$x\times z=-y$,则得到$y$轴负方向,因此需改为顺时针,即$-\theta$,这无论是在左手坐标系还是右手系都是相同的。</p><h4 id="4-3-3-三维绕任意轴旋转"><a href="#4-3-3-三维绕任意轴旋转" class="headerlink" title="4.3.3 三维绕任意轴旋转"></a>4.3.3 三维绕任意轴旋转</h4><p> <strong>向量$P$绕任意轴$A$旋转$\theta$角度证明:</strong></p><p> 不妨设$A$为单位向量,同时$P$可分解为与$A$平行和垂直的两个分量,分别为:</p><script type="math/tex; mode=display">\begin{align}P_{projA}&=(A\cdot P)A=AA^TP\\P_{perpA}&=P-(A\cdot P)A=P-AA^TP\end{align}</script><p>如图所示</p><img src="/2020/07/19/Mathematics-for-3D-Game-Programming-and-Computer-Graphics/image-20200606161620614.png" class="" title="image-20200606161620614"><p>故最终结果</p><script type="math/tex; mode=display">P^{'}=P_{perpA}^{'}+P_{ProjA}</script><p>其中<script type="math/tex">P_{PerpA}^{'}</script>为<script type="math/tex">P_{perpA}</script>旋转<script type="math/tex">\theta</script>后得到的向量,如图</p><img src="/2020/07/19/Mathematics-for-3D-Game-Programming-and-Computer-Graphics/image-20200801231223987.png" class="" title="image-20200801231223987"><p>为求得其值,需找到一组线性组合来表示它,可选$P<em>{perpA}$与其旋转90°后的向量这两个向量作为组合,可以得知$A\times P$即为该向量,同时它的长度是与$P</em>{perpA}$相等的,这是因为</p><script type="math/tex; mode=display">|A\times P|=|P|sin\alpha=|P-(A\cdot P)A|</script><p>所以</p><script type="math/tex; mode=display">P_{perpA}^{'}=[P-(A\cdot P)A]cos\theta+(A \times P)sin \theta</script><p>所以</p><script type="math/tex; mode=display">\begin{align}P^{'}&=[P-(A\cdot P)A]cos\theta+(A \times P)sin \theta+(A\cdot P)A\\&=Pcos\theta+A\times P sin\theta+(A\cdot P)A(1-cos\theta)\end{align}</script><p>记$I$为单位阵,则矩阵形式为</p><script type="math/tex; mode=display">P^{'}=\{Icos\theta+\begin{bmatrix} 0&-A_z&A_y\\A_z&0&-A_x\\-A_y&A_x&0\end{bmatrix}sin\theta+AA^T(1-cos\theta)\}P</script><p>其中记</p><script type="math/tex; mode=display">R=Icos\theta+\begin{bmatrix} 0&-A_z&A_y\\A_z&0&-A_x\\-A_y&A_x&0\end{bmatrix}sin\theta+AA^T(1-cos\theta)</script><p>为罗德里格斯旋转公式,而将其展开写成一个矩阵就可得到旋转矩阵。</p><h3 id="4-4-齐次坐标"><a href="#4-4-齐次坐标" class="headerlink" title="4.4 齐次坐标"></a>4.4 齐次坐标</h3><p> 引入第四维度,即四维矩阵$F$得描述来取代之前得三维矩阵的线性变换,这两者是等效的。</p><script type="math/tex; mode=display">F=\begin{bmatrix}M_{33}&\vdots&T_{13}\\0&\vdots&1\end{bmatrix}</script><p>这是一个分块矩阵,由之前线性变换中的三维矩阵$M$和三维向量$T$组成,同时我们用${x,y,z,1}$代表点,${x,y,z,1}$代表向量。</p><h3 id="4-5-法向量变换"><a href="#4-5-法向量变换" class="headerlink" title="4.5 法向量变换"></a>4.5 法向量变换</h3><p> 当物体经过变换后,其原表面上的法向量就不一定与原来相同了,尤其是发生形变后。不妨设$n,t$分别为变换前的法向量与切向量,$n^{‘},t^{‘}$为变换后正确的法向量与切向量。则</p><script type="math/tex; mode=display">\begin{align}t^Tn=0\\t^TM^T(M^T)^{-1}n=0\\(Mt)^T(M^{-1})^Tn=0\\t^{'T}n^{'}=0\end{align}</script><p>即切向量的变换方法为左乘$(M^{-1})^T$,那么如果$M$为正交阵,法向量的变换为$Mn$,这也就是说,如果变换时不改变物体的大小形状,法向量的变换是与物体相同的。</p><h3 id="4-6-四元数"><a href="#4-6-四元数" class="headerlink" title="4.6 四元数"></a>4.6 四元数</h3><p> $q=(w,x,y,z)=w+xi+yj+zk$为四元数,有实部和虚部组成。四元数$q=s+v$的共轭为$\bar q=s-v$,</p><p>有,</p><script type="math/tex; mode=display">q\bar q=\bar q q=|q|^2=q^2</script><p>故$q$的倒数$q^{-1}$为:</p><script type="math/tex; mode=display">q^{-1}=\frac{\bar q}{|q|^2}</script><p> 三维中P点的旋转描述为:</p><script type="math/tex; mode=display">\varphi(P)=qPq^{-1}</script><p>其中,$q=cos\frac{\theta}{2}+Asin\frac{\theta}{2}$,${A}$为任意单位旋转轴,$\theta$为旋转角度。函数$\varphi$为同态函数,其映射满足长度,角度,偏手性不变。</p><h4 id="4-6-1-球面线性插值"><a href="#4-6-1-球面线性插值" class="headerlink" title="4.6.1 球面线性插值"></a>4.6.1 球面线性插值</h4><p> 普通线性插值方法:</p><script type="math/tex; mode=display">q(t)=\frac{(1-t)q_1+tq_2}{||(1-t)q_1+tq_2||}</script><p> </p><p>其变化率可用$cos^{-1}(q(t)\cdot q_1)$表示,由于其随$t$的变化率不为常数,因此旋转不是匀速的。</p><p> 引入球面线性插值:</p><script type="math/tex; mode=display">q(t)=\frac{sin[(1-t)\theta]}{sin\theta}q_1+\frac{sin(t\theta)}{sin\theta}q_2</script><p>其中$\theta=cos^{-1}(q_1\cdot q_2)$,也可用$sin\theta=\sqrt{1-(q_1\cdot q_2)^2}$来计算。</p>]]></content>
<categories>
<category>笔记</category>
</categories>
<tags>
<tag>Graphics</tag>
</tags>
</entry>
<entry>
<title>Quadric error metric</title>
<link href="/2020/07/14/2020-07-14-Quadric-error-metric/"/>
<url>/2020/07/14/2020-07-14-Quadric-error-metric/</url>
<content type="html"><![CDATA[<h3 id="1-推导"><a href="#1-推导" class="headerlink" title="1.推导"></a>1.推导</h3><p>  (以下向量均为列向量)</p><p>  设平面$p_i$的法向量为$n_i$,$x_i$为该平面上任一点,则其在齐次坐标下的方程为:</p><script type="math/tex; mode=display">\bar n_i=(n_i,-n_i \cdot x_i)</script><p>设空间中任意一点$x$的齐次坐标为$\bar x=(x, 1)$</p><img src="/2020/07/14/2020-07-14-Quadric-error-metric/image-20200714231049940.png" class="" title="image-20200714231049940"><p>由几何关系可知$x$到$p_i$的距离的平方$d(x,p_i)$为:</p><script type="math/tex; mode=display">\begin{align}d(x,p_i)&=((x-x_i)\cdot n_i)^2\\&=(\begin{bmatrix}n_i,-n_i\cdot x_i\end{bmatrix}\begin{bmatrix}x\\1\end{bmatrix})^2\\&=(\bar n_i^T\bar x)^2\\&=\bar x^T(\bar n_i\bar n_i^T) \bar x\\&=\bar x^TQ_i\bar x\end{align}</script><p>其中$Q_i$称为Quadratic error Matrix,注意到这个$Q_i$由于是法向量生成的,所以是定义面上的,现在分别定义顶点和边上的Quadratic error Matrix $Q_i^v$和$Q^e$:</p><script type="math/tex; mode=display">\begin{align}Q_i^v&=\sum_{j\in \Omega(i)}Q_j\\Q^e&=Q_1^v+Q_2^v\end{align}</script><img src="/2020/07/14/2020-07-14-Quadric-error-metric/image-20200714233952827.png" class="" title="image-20200714233952827"><p>所以每条边上的 $\bar v$要满足</p><script type="math/tex; mode=display">\bar v=min(v^TQ^ev)</script><p>若$Q^e$可逆,记$\Delta=v^TQ^ev$,当取最小值时,应该满足$\frac{\partial \Delta}{\partial x}=0$,$\frac{\partial \Delta}{\partial y}=0$,$\frac{\partial \Delta}{\partial z}=0$,即:</p><script type="math/tex; mode=display">\left[\begin{array}{cccc}q_{11} & q_{12} & q_{13} & q_{14} \\q_{12} & q_{22} & q_{23} & q_{24} \\q_{13} & q_{23} & q_{33} & q_{34} \\0 & 0 & 0 & 1\end{array}\right] \mathbf{\mathbf { \bar v }}=\left[\begin{array}{c}0 \\0 \\0 \\1\end{array}\right]</script><p>(ps:如果$Q^e$不满秩,则可取$\bar v=\frac{v_1+v_2}{2}$,即为两顶点中点)</p><h3 id="2-算法流程"><a href="#2-算法流程" class="headerlink" title="2. 算法流程"></a>2. 算法流程</h3><p> 输入:网格,目标定点数n,cost阈值t</p><p> 输出:简化后的网格</p><ol><li><p>计算每条边的 $Q^e$</p></li><li><p>计算每条边上的新的$\bar v$</p></li><li><p>while <script type="math/tex">N_v</script> >n and <script type="math/tex">Cost_{min}</script><t: </p><p> 将每条边的$cost=\bar v^TQ^e\bar v$放入最小堆;</p><p> 删除堆顶的cost,并且塌陷该条边(即将该边的两个顶点并到$\bar v$上去,该新点上的Quadratic error Matrix则更新为刚才的$Q^e$);</p><p> 更新堆(更新相邻的$Q^e$和cost); </p><p>end;</p></li></ol>]]></content>
<categories>
<category>算法</category>
</categories>
<tags>
<tag>算法</tag>
</tags>
</entry>
<entry>
<title>Shader笔记1</title>
<link href="/2020/07/02/2020-07-02-Shader/"/>
<url>/2020/07/02/2020-07-02-Shader/</url>
<content type="html"><![CDATA[<h2 id="Vector"><a href="#Vector" class="headerlink" title="Vector"></a>Vector</h2><div class="table-container"><table><thead><tr><th>类型</th><th>含义</th></tr></thead><tbody><tr><td><code>vecn</code></td><td>包含<code>n</code>个float分量的默认向量</td></tr><tr><td><code>bvecn</code></td><td>包含<code>n</code>个bool分量的向量</td></tr><tr><td><code>ivecn</code></td><td>包含<code>n</code>个int分量的向量</td></tr><tr><td><code>uvecn</code></td><td>包含<code>n</code>个unsigned int分量的向量</td></tr><tr><td><code>dvecn</code></td><td>包含<code>n</code>个double分量的向量</td></tr></tbody></table></div><h3 id="Swizzling-重组"><a href="#Swizzling-重组" class="headerlink" title="Swizzling(重组)"></a>Swizzling(重组)</h3><pre><code class="hljs glsl"><span class="hljs-type">vec2</span> someVec;<span class="hljs-type">vec4</span> differentVec = someVec.xyxx;<span class="hljs-type">vec3</span> anotherVec = differentVec.zyw;<span class="hljs-type">vec4</span> otherVec = someVec.xxxx + anotherVec.yxzy;</code></pre><h2 id="Uniform"><a href="#Uniform" class="headerlink" title="Uniform"></a>Uniform</h2><p>  Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。</p><pre><code class="hljs glsl"><span class="hljs-meta">#version 330 core</span><span class="hljs-keyword">out</span> <span class="hljs-type">vec4</span> color;<span class="hljs-keyword">uniform</span> <span class="hljs-type">vec4</span> ourColor; <span class="hljs-comment">// 在OpenGL程序代码中设定这个变量</span><span class="hljs-type">void</span> main(){ color = ourColor;}</code></pre><h3 id="传值"><a href="#传值" class="headerlink" title="传值"></a>传值</h3><p>  通过opengl向uniform变量传值。用<code>glGetUniformLocation</code>查询uniform <code>ourColor</code>的位置值。如果返回<code>-1</code>就代表没有找到这个位置值。最后,我们可以通过glUniform4f函数设置uniform值。注意,查询uniform地址不要求你之前使用过着色器程序,但是更新一个unform之前你<strong>必须</strong>先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置unform的。</p><pre><code class="hljs glsl">GLfloat timeValue = glfwGetTime();GLfloat greenValue = (<span class="hljs-built_in">sin</span>(timeValue) / <span class="hljs-number">2</span>) + <span class="hljs-number">0.5</span>;GLint vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");glUseProgram(shaderProgram);<span class="hljs-comment">//必须先使用程序</span>glUniform4f(vertexColorLocation, <span class="hljs-number">0.0</span>f, greenValue, <span class="hljs-number">0.0</span>f, <span class="hljs-number">1.0</span>f);</code></pre>]]></content>
<categories>
<category>Shader</category>
</categories>
<tags>
<tag>Shader</tag>
<tag>GLSL</tag>
</tags>
</entry>
<entry>
<title>OpenGL笔记1</title>
<link href="/2020/07/01/2020-07-01-OpenGL%E7%AC%94%E8%AE%B01/"/>
<url>/2020/07/01/2020-07-01-OpenGL%E7%AC%94%E8%AE%B01/</url>
<content type="html"><![CDATA[<h2 id="1-初始化"><a href="#1-初始化" class="headerlink" title="1.初始化"></a>1.初始化</h2><pre><code class="hljs c++">glewExperimental = GL_TRUE;<span class="hljs-comment">//让GLEW在管理OpenGL的函数指针时更多地使用现代化的技术</span><span class="hljs-keyword">if</span> (<span class="hljs-built_in">glewInit</span>() != GLEW_OK)<span class="hljs-comment">//初始化</span>{ std::cout << <span class="hljs-string">"Failed to initialize GLEW"</span> << std::endl; <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;}</code></pre><div class="note note-warning"> <p>如果是以静态库调用glew,务必在开头加上预编译的头<code>#define GLEW_STATIC</code></p> </div><h2 id="2-基本流程"><a href="#2-基本流程" class="headerlink" title="2.基本流程"></a>2.基本流程</h2><p>  首先创建一个对象,然后用一个id保存它的引用(实际数据被储存在后台)。然后将对象绑定至上下文的目标位置(例子中窗口对象目标的位置被定义成<code>GL_WINDOW_TARGET</code>)。接下来设置窗口的选项。最后将目标位置的对象id设回0,解绑这个对象。设置的选项将被保存在<code>objectId</code>所引用的对象中,一旦我们重新绑定这个对象到<code>GL_WINDOW_TARGET</code>位置,这些选项就会重新生效。</p><pre><code class="hljs c++"><span class="hljs-comment">// 创建对象</span>GLuint objectId = <span class="hljs-number">0</span>;<span class="hljs-built_in">glGenObject</span>(<span class="hljs-number">1</span>, &objectId);<span class="hljs-comment">// 绑定对象至上下文</span><span class="hljs-built_in">glBindObject</span>(GL_WINDOW_TARGET, objectId);<span class="hljs-comment">// 设置当前绑定到 GL_WINDOW_TARGET 的对象的一些选项</span><span class="hljs-built_in">glSetObjectOption</span>(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, <span class="hljs-number">800</span>);<span class="hljs-built_in">glSetObjectOption</span>(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, <span class="hljs-number">600</span>);<span class="hljs-comment">// 将上下文对象设回默认</span><span class="hljs-built_in">glBindObject</span>(GL_WINDOW_TARGET, <span class="hljs-number">0</span>);</code></pre><div class="note note-info"> <p>简单的理解就是像创建id,然后绑定至OpenGL中的对象,这两步也可以近似理解为实例化一个对象,然后就可以对这个对象进行操作。</p> </div><h2 id="3-FSM"><a href="#3-FSM" class="headerlink" title="3. FSM"></a>3. FSM</h2><p>  OpenGL的系统是一个有限状态机,因此其函数有两种,状态设置函数(State-changing Function)和状态应用函数(State-using Function),前者改变OpenGL的状态,只有当进入相应状态,然后执行该状态下的应用函数才会起作用。</p><p>比如</p><pre><code class="hljs c++"><span class="hljs-built_in">glClearColor</span>(<span class="hljs-number">0.2f</span>, <span class="hljs-number">0.3f</span>, <span class="hljs-number">0.3f</span>, <span class="hljs-number">1.0f</span>);<span class="hljs-comment">//rgb(范围0-1),alpha channel 状态设置函数</span><span class="hljs-built_in">glClear</span>(GL_COLOR_BUFFER_BIT);<span class="hljs-comment">//状态应用函数,真正应用清除颜色</span></code></pre><h2 id="4-VBO"><a href="#4-VBO" class="headerlink" title="4. VBO"></a>4. VBO</h2><p>  顶点缓冲对象为cpu->gpu的图形的顶点数据,简而言之,通过缓冲一次传一批顶点。</p><pre><code class="hljs c++">GLuint VBO;<span class="hljs-built_in">glGenBuffers</span>(<span class="hljs-number">1</span>, &VBO); <span class="hljs-comment">//获得VBO id</span><span class="hljs-built_in">glBindBuffer</span>(GL_ARRAY_BUFFER, VBO);<span class="hljs-comment">//绑定顶点缓冲 </span><span class="hljs-built_in">glBufferData</span>(GL_ARRAY_BUFFER, <span class="hljs-built_in">sizeof</span>(vertices), vertices, GL_STATIC_DRAW);<span class="hljs-comment">//绑定顶点数据,这一步才真正将顶点数据vertices与buffer绑定</span></code></pre><p>介绍一下第四个参数</p><ul><li><code>GL_STATIC_DRAW</code> :数据不会或几乎不会改变。</li><li><code>GL_DYNAMIC_DRAW</code>:数据会被改变很多。</li><li><code>GL_STREAM_DRAW</code> :数据每次绘制时都会改变。</li></ul><h2 id="5-Vertex-Shader"><a href="#5-Vertex-Shader" class="headerlink" title="5.Vertex Shader"></a>5.Vertex Shader</h2><pre><code class="hljs glsl"><span class="hljs-meta">#version 330 core //声明版本</span><span class="hljs-keyword">layout</span> (<span class="hljs-keyword">location</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">in</span> <span class="hljs-type">vec3</span> position;<span class="hljs-comment">//in关键字声明输入变量的属性,layout(location=x)为输入变量的位置</span><span class="hljs-type">void</span> main(){ <span class="hljs-built_in">gl_Position</span> = <span class="hljs-type">vec4</span>(position.x, position.y, position.z, <span class="hljs-number">1.0</span>);<span class="hljs-comment">//输出给后面,因为输入的是三维向量,将其改成后面需要的齐次坐标形式</span>}</code></pre><div class="note note-info"> <p>Shader 我理解为将一道完整的工序中独立出一个步骤,而这个步骤是由用户自定的,这个步骤有上一道步骤的输入,而我们可以在这里进行编程操作,只要保证输出是符合下一道步骤的输入即可。有点面向切面的味道。</p> </div><h3 id="5-1-编译Shader"><a href="#5-1-编译Shader" class="headerlink" title="5.1.编译Shader"></a>5.1.编译Shader</h3><pre><code class="hljs c++">GLuint vertexShader;vertexShader = <span class="hljs-built_in">glCreateShader</span>(GL_VERTEX_SHADER);<span class="hljs-comment">//创建一个shader id</span><span class="hljs-comment">//绑定shader代码,参数一次为id,shader 数量,shader代码,第四个如果不为null,它会返回源码数组相应的长度数组</span><span class="hljs-built_in">glShaderSource</span>(vertexShader, <span class="hljs-number">1</span>, &vertexShaderSource, <span class="hljs-literal">NULL</span>);<span class="hljs-built_in">glCompileShader</span>(vertexShader);<span class="hljs-comment">//编译</span></code></pre><h2 id="6-Fragment-Shader"><a href="#6-Fragment-Shader" class="headerlink" title="6.Fragment Shader"></a>6.Fragment Shader</h2><p>用于颜色</p><pre><code class="hljs glsl"><span class="hljs-meta">#version 330 core</span><span class="hljs-keyword">out</span> <span class="hljs-type">vec4</span> color;<span class="hljs-comment">//out 关键字声明输出该变量</span><span class="hljs-type">void</span> main(){ color = <span class="hljs-type">vec4</span>(<span class="hljs-number">1.0</span>f, <span class="hljs-number">0.5</span>f, <span class="hljs-number">0.2</span>f, <span class="hljs-number">1.0</span>f);<span class="hljs-comment">//输出颜色</span>}</code></pre><h3 id="6-1-编译Shader"><a href="#6-1-编译Shader" class="headerlink" title="6.1 编译Shader"></a>6.1 编译Shader</h3><pre><code class="hljs c++">GLuint fragmentShader;fragmentShader = <span class="hljs-built_in">glCreateShader</span>(GL_FRAGMENT_SHADER);<span class="hljs-built_in">glShaderSource</span>(fragmentShader, <span class="hljs-number">1</span>, &fragmentShaderSource, null);<span class="hljs-built_in">glCompileShader</span>(fragmentShader);</code></pre><h2 id="7-Shader-Program-Object"><a href="#7-Shader-Program-Object" class="headerlink" title="7.Shader Program Object"></a>7.Shader Program Object</h2><p>  要使用刚才编译的着色器我们必须把它们链接为一个着色器程序对象,然后在渲染对象的时候激活这个着色器程序。已激活着色器程序的着色器将在我们发送渲染调用的时候被使用。当链接着色器至一个程序的时候,它会把每个着色器的输出链接到下个着色器的输入。当输出和输入不匹配的时候,你会得到一个连接错误。</p><pre><code class="hljs c++">GLuint shaderProgram;shaderProgram = <span class="hljs-built_in">glCreateProgram</span>();<span class="hljs-comment">//创建一个程序对象</span><span class="hljs-built_in">glAttachShader</span>(shaderProgram, vertexShader);<span class="hljs-built_in">glAttachShader</span>(shaderProgram, fragmentShader);<span class="hljs-comment">//将shader附加至Program</span><span class="hljs-built_in">glLinkProgram</span>(shaderProgram);<span class="hljs-comment">//将gl与该program链接,即渲染时采用这个program</span><span class="hljs-comment">//激活使用该着色器对象,调用之后,每个着色器调用和渲染调用都会使用这个程序对象</span><span class="hljs-built_in">glUseProgram</span>(shaderProgram);<span class="hljs-comment">//链接shader到program后别忘记删除</span><span class="hljs-built_in">glDeleteShader</span>(vertexShader);<span class="hljs-built_in">glDeleteShader</span>(fragmentShader);</code></pre><h2 id="8-读取顶点(解释自定义的顶点)"><a href="#8-读取顶点(解释自定义的顶点)" class="headerlink" title="8.读取顶点(解释自定义的顶点)"></a>8.读取顶点(解释自定义的顶点)</h2><p>  如果是自定义的顶点数据,该如何解释也是一个问题,可以使用下面的方法解决</p><pre><code class="hljs c++"><span class="hljs-comment">// 0. 复制顶点数组到缓冲中供OpenGL使用</span><span class="hljs-built_in">glBindBuffer</span>(GL_ARRAY_BUFFER, VBO);<span class="hljs-built_in">glBufferData</span>(GL_ARRAY_BUFFER, <span class="hljs-built_in">sizeof</span>(vertices), vertices, GL_STATIC_DRAW);<span class="hljs-comment">// 1. 设置顶点属性指针</span><span class="hljs-built_in">glVertexAttribPointer</span>(<span class="hljs-number">0</span>, <span class="hljs-number">3</span>, GL_FLOAT, GL_FALSE, <span class="hljs-number">3</span> * <span class="hljs-built_in">sizeof</span>(GLfloat), (GLvoid*)<span class="hljs-number">0</span>);<span class="hljs-built_in">glEnableVertexAttribArray</span>(<span class="hljs-number">0</span>);<span class="hljs-comment">// 2. 当我们渲染一个物体时要使用着色器程序</span><span class="hljs-built_in">glUseProgram</span>(shaderProgram);<span class="hljs-comment">// 3. 绘制物体</span><span class="hljs-built_in">someOpenGLFunctionThatDrawsOurTriangle</span>();</code></pre><p><code>glVertexAttribPointer</code>函数的参数:</p><ul><li>第一个参数指定配置的顶点属性。比如在顶点着色器中使用<code>layout(location = 0)</code>定义了position顶点属性的位置值(Location)它可以把顶点属性的位置值设置为<code>0</code>。因为我们希望把数据传递到这一个顶点属性中,所以这里传入<code>0</code>。</li><li>第二个参数指定顶点属性的大小。顶点属性是一个<code>vec3</code>,它由3个值组成,所以大小是3。</li><li>第三个参数指定数据的类型,这里是<code>GL_FLOAT</code>(GLSL中<code>vec*</code>都是由浮点数值组成的)。</li><li>第四个参数定义是否希望数据被标准化(Normalize)。如果我们设置为<code>GL_TRUE</code>,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。它设置为GL_FALSE。</li><li>第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。由于下个组位置数据在3个<code>GLfloat</code>之后,我们把步长设置为<code>3 * sizeof(GLfloat)</code>。</li><li>最后一个参数的类型是<code>GLvoid*</code>,所以需要进行这个奇怪的强制类型转换。它表示位置数据在缓冲中起始位置的偏移量(Offset)。由于位置数据在数组的开头,所以这里是0。</li></ul><div class="note note-success"> <p>每个顶点属性从一个VBO管理的内存中获得它的数据,而具体是从哪个VBO(程序中可以有多个VBO)获取则是通过在调用<code>glVetexAttribPointer</code>时绑定到<code>GL_ARRAY_BUFFER</code>的VBO决定的。由于在调用<code>glVetexAttribPointer</code>之前绑定的是先前定义的VBO对象,顶点属性<code>0</code>现在会链接到它的顶点数据。</p> </div><h2 id="9-VAO"><a href="#9-VAO" class="headerlink" title="9.VAO"></a>9.VAO</h2><p>  顶点数组对象(Vertex Array Object, VAO)可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。刚刚设置的所有状态都将存储在VAO中</p><pre><code class="hljs c++">GLuint VAO;<span class="hljs-built_in">glGenVertexArrays</span>(<span class="hljs-number">1</span>, &VAO); <span class="hljs-comment">//创建VAO</span><span class="hljs-comment">// ..:: 初始化代码(只运行一次 (除非你的物体频繁改变)) :: ..</span><span class="hljs-comment">// 1. 绑定VAO</span><span class="hljs-built_in">glBindVertexArray</span>(VAO);<span class="hljs-comment">// 2. 把顶点数组复制到缓冲中供OpenGL使用</span><span class="hljs-built_in">glBindBuffer</span>(GL_ARRAY_BUFFER, VBO);<span class="hljs-built_in">glBufferData</span>(GL_ARRAY_BUFFER, <span class="hljs-built_in">sizeof</span>(vertices), vertices, GL_STATIC_DRAW);<span class="hljs-comment">// 3. 设置顶点属性指针</span><span class="hljs-built_in">glVertexAttribPointer</span>(<span class="hljs-number">0</span>, <span class="hljs-number">3</span>, GL_FLOAT, GL_FALSE, <span class="hljs-number">3</span> * <span class="hljs-built_in">sizeof</span>(GLfloat), (GLvoid*)<span class="hljs-number">0</span>);<span class="hljs-built_in">glEnableVertexAttribArray</span>(<span class="hljs-number">0</span>);<span class="hljs-comment">//4. 解绑VAO,相当于买完东西后离开,这个东西自己拿去用(比如在绘制东西时再绑定上去)</span><span class="hljs-built_in">glBindVertexArray</span>(<span class="hljs-number">0</span>);<span class="hljs-comment">// 5. 绘制物体</span><span class="hljs-built_in">glUseProgram</span>(shaderProgram);<span class="hljs-comment">//使用shader渲染</span><span class="hljs-built_in">glBindVertexArray</span>(VAO);<span class="hljs-comment">//再绑定</span><span class="hljs-built_in">glDrawArrays</span>(GL_TRIANGLES, <span class="hljs-number">0</span>, <span class="hljs-number">3</span>);<span class="hljs-comment">//形状,起始点,顶点数量</span><span class="hljs-built_in">glBindVertexArray</span>(<span class="hljs-number">0</span>);<span class="hljs-comment">//解绑</span></code></pre><p>  流程是:打算绘制多个物体时,首先要生成/配置所有的VAO(和必须的VBO及属性指针),然后储存它们供后面使用。当要绘制物体的时候就拿出相应的VAO,绑定它,绘制完物体后,再解绑VAO。</p><h2 id="10-EBO"><a href="#10-EBO" class="headerlink" title="10.EBO"></a>10.EBO</h2><p>  索引缓冲对象,通过索引来重用顶点。</p><pre><code class="hljs c++"><span class="hljs-built_in">glDrawArrays</span>(GL_TRIANGLES, <span class="hljs-number">0</span>, <span class="hljs-number">3</span>);GLuint EBO;<span class="hljs-built_in">glGenBuffers</span>(<span class="hljs-number">1</span>, &EBO);<span class="hljs-comment">//创建对象id</span><span class="hljs-built_in">glBindBuffer</span>(GL_ELEMENT_ARRAY_BUFFER, EBO);<span class="hljs-built_in">glBufferData</span>(GL_ELEMENT_ARRAY_BUFFER,<span class="hljs-built_in">sizeof</span>(indices),indices,GL_STATIC_DRAW); <span class="hljs-comment">//绑定索引</span><span class="hljs-comment">//此时,绘制方法改变,即不再使用glDrawArrays</span><span class="hljs-built_in">glBindBuffer</span>(GL_ELEMENT_ARRAY_BUFFER, EBO);<span class="hljs-comment">//绑定EBO</span><span class="hljs-built_in">glDrawElements</span>(GL_TRIANGLES, <span class="hljs-number">6</span>, GL_UNSIGNED_INT, <span class="hljs-number">0</span>);<span class="hljs-comment">//通过索引方法来绘制,形状,顶点数,索引类型,EBO偏移量</span></code></pre><h2 id="11-线框模式"><a href="#11-线框模式" class="headerlink" title="11.线框模式"></a>11.线框模式</h2><p>  要想用线框模式绘制你的三角形,可以通过<code>glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)</code>函数配置OpenGL如何绘制图元。第一个参数表示将其应用到所有的三角形的正面和背面,第二个参数表示用线来绘制。之后的绘制调用会一直以线框模式绘制三角形,直到我们用<code>glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)</code>将其设置回默认模式。</p><h2 id="12-总结"><a href="#12-总结" class="headerlink" title="12.总结"></a>12.总结</h2><p>  在绑定VAO时如果绑定了EBO,VAO也会存储索引。当目标是<code>GL_ELEMENT_ARRAY_BUFFER</code>的时候,VAO会储存<code>glBindBuffer</code>的函数调用。这也意味着它也会储存解绑调用,所以确保你没有在解绑VAO之前解绑索引数组缓冲,否则它就没有这个EBO配置了。</p><pre><code class="hljs c++"><span class="hljs-comment">// ..:: 初始化代码 :: ..</span><span class="hljs-comment">// 1. 绑定顶点数组对象</span><span class="hljs-built_in">glBindVertexArray</span>(VAO);<span class="hljs-comment">// 2. 把我们的顶点数组复制到一个顶点缓冲中,供OpenGL使用</span><span class="hljs-built_in">glBindBuffer</span>(GL_ARRAY_BUFFER, VBO);<span class="hljs-built_in">glBufferData</span>(GL_ARRAY_BUFFER, <span class="hljs-built_in">sizeof</span>(vertices), vertices, GL_STATIC_DRAW);<span class="hljs-comment">// 3. 复制索引数组到一个索引缓冲中,供OpenGL使用</span><span class="hljs-built_in">glBindBuffer</span>(GL_ELEMENT_ARRAY_BUFFER, EBO);<span class="hljs-built_in">glBufferData</span>(GL_ELEMENT_ARRAY_BUFFER, <span class="hljs-built_in">sizeof</span>(indices), indices, GL_STATIC_DRAW);<span class="hljs-comment">// 3. 设定顶点属性指针</span><span class="hljs-built_in">glVertexAttribPointer</span>(<span class="hljs-number">0</span>, <span class="hljs-number">3</span>, GL_FLOAT, GL_FALSE, <span class="hljs-number">3</span> * <span class="hljs-built_in">sizeof</span>(GLfloat), (GLvoid*)<span class="hljs-number">0</span>);<span class="hljs-built_in">glEnableVertexAttribArray</span>(<span class="hljs-number">0</span>);<span class="hljs-comment">// 4. 解绑VAO(不是EBO!)</span><span class="hljs-built_in">glBindVertexArray</span>(<span class="hljs-number">0</span>);<span class="hljs-comment">//...</span><span class="hljs-comment">// ..:: 绘制代码(游戏循环中) :: ..</span><span class="hljs-built_in">glUseProgram</span>(shaderProgram);<span class="hljs-built_in">glBindVertexArray</span>(VAO);<span class="hljs-built_in">glDrawElements</span>(GL_TRIANGLES, <span class="hljs-number">6</span>, GL_UNSIGNED_INT, <span class="hljs-number">0</span>)<span class="hljs-built_in">glBindVertexArray</span>(<span class="hljs-number">0</span>);</code></pre>]]></content>
<categories>
<category>OpenGL</category>
</categories>
<tags>
<tag>OpenGL</tag>
</tags>
</entry>
<entry>
<title>VS项目配置项说明</title>
<link href="/2020/06/28/2020-06-28-VS%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE%E9%A1%B9%E8%AF%B4%E6%98%8E/"/>
<url>/2020/06/28/2020-06-28-VS%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE%E9%A1%B9%E8%AF%B4%E6%98%8E/</url>
<content type="html"><![CDATA[<p> 记录一下vs配置时项目的属性设置,主要是引入三方库时需要用到,不同于cmake这种通过配置文件来操作的,感觉vs这种通过鼠标来设置又蠢又麻烦。。。</p><ul><li>VC++目录:<ul><li>可执行目录:寻找依赖的可执行的文件的目录</li><li>包含目录:寻找#include<xxxx.h>中的xxxx.h的搜索目录</li><li>库目录:寻找.lib文件的搜索目录</li></ul></li><li><p>C/C++:</p><ul><li>常规->附加包含目录:寻找#include<xxxx.h>中的xxxx.h的搜索目录(每一项对应一个文件夹XXXX,文件夹中包含了编译时所需的头文件,使用时直接#include<XXXX>即可)</li></ul></li><li><p>链接器:</p><ul><li>常规->附加库目录:寻找.lib文件的搜索目录</li><li>输入->附加依赖项:lib库(C++的库会把函数、类的声明放在*.h中,实现放在*.cpp或* .cc中。编译之后,*.cpp,*.cc,*.c会被打包成一个.lib文件,这样可以保护源代码)</li></ul></li></ul><p>最终方法为:</p><ol><li>附加包含目录—-添加工程的头文件目录: 项目->属性->配置属性->C/C++->常规->附加包含目录:加上头文件的存放目录; </li><li>附加库目录—-添加文件引用的lib静态库路径: 项目->属性->配置属性->链接器->常规->附加库目录:加上lib文件的存放目录; </li><li>附加依赖项—-添加工程引用的lib文件名: 项目->属性->配置属性->链接器->输入->附加依赖项:加上lib文件名。</li></ol><p> <strong>ps</strong>:包含目录:修改了系统的include宏的值,是全局的; 附加包含目录:用于当前项目,对其他项目没有影响。</p><img src="/2020/06/28/2020-06-28-VS%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE%E9%A1%B9%E8%AF%B4%E6%98%8E/image-20200628225451830.png" class="" title="image-20200628225451830"><img src="/2020/06/28/2020-06-28-VS%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE%E9%A1%B9%E8%AF%B4%E6%98%8E/image-20200628225507115.png" class="" title="image-20200628225507115"><img src="/2020/06/28/2020-06-28-VS%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE%E9%A1%B9%E8%AF%B4%E6%98%8E/image-20200628225519543.png" class="" title="image-20200628225519543"><img src="/2020/06/28/2020-06-28-VS%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE%E9%A1%B9%E8%AF%B4%E6%98%8E/image-20200628225540868.png" class="" title="image-20200628225540868"><p><a href="https://blog.csdn.net/raodotcong/article/details/8998379">更详细的文章</a></p>]]></content>
<categories>
<category>配置</category>
</categories>
<tags>
<tag>配置</tag>
</tags>
</entry>
<entry>
<title>Blinn-Phong Reflectance Model</title>
<link href="/2020/06/17/2020-06-17-Blinn-Phong-Reflectance-Model/"/>
<url>/2020/06/17/2020-06-17-Blinn-Phong-Reflectance-Model/</url>
<content type="html"><![CDATA[<h2 id="光照模型"><a href="#光照模型" class="headerlink" title="光照模型"></a>光照模型</h2><script type="math/tex; mode=display">L=L_d+L_s+L_a</script><p>$L_d$为漫反射光,$L_s$为高光,$L_a$为环境光。</p><h3 id="1-漫反射"><a href="#1-漫反射" class="headerlink" title="1.漫反射"></a>1.漫反射</h3><p>理解:漫反射散向四面八方,与观察方向无关,只与表面接受到的光源的<strong>有效光</strong>有关</p><script type="math/tex; mode=display">L_d=k_d\frac{I}{r^2}max\{0,n\cdot l\}</script><p>$k_d$漫反射系数(范围0至1,表明反射的强度),$I$光源,$r$表面距光源距离,$n$表明法向量,$l$入射光向量(注意方向为负)</p><p>ps:均为单位向量</p><h3 id="2-高光"><a href="#2-高光" class="headerlink" title="2.高光"></a>2.高光</h3><p>相比于Phong模型,Blinn-Phong模型的高光项采用$l,v$两者的角平分线与$n$的夹角来表示观察方向与镜面反射方向的夹角,从而提高计算效率。</p><script type="math/tex; mode=display">L_s=k_s\frac{I}{r^2}max\{0,n\cdot h\}^p</script><p>$k_s$镜面反射系数(范围0至1,表明反射的强度),$I$光源,$r$表面距光源距离,$n$表明法向量,$h=\frac{l+v}{|l+v|}$,$p$表示角度的影响,因为高光只在小角度才会起作用,下面是$cos^px$的几个变化图</p><h3 id="3-环境光"><a href="#3-环境光" class="headerlink" title="3.环境光"></a>3.环境光</h3><p>在Blinn-Phong模型中环境光被当作常数处理</p><script type="math/tex; mode=display">L_a=k_aI_a</script><h3 id="4-展示"><a href="#4-展示" class="headerlink" title="4. 展示"></a>4. 展示</h3>]]></content>
<categories>
<category>算法</category>
</categories>
<tags>
<tag>算法</tag>
</tags>
</entry>
<entry>
<title>旋转变换矩阵推导</title>
<link href="/2020/06/06/2020-06-06-%E6%97%8B%E8%BD%AC%E5%8F%98%E6%8D%A2%E7%9F%A9%E9%98%B5%E6%8E%A8%E5%AF%BC/"/>
<url>/2020/06/06/2020-06-06-%E6%97%8B%E8%BD%AC%E5%8F%98%E6%8D%A2%E7%9F%A9%E9%98%B5%E6%8E%A8%E5%AF%BC/</url>
<content type="html"><![CDATA[<p> </p><h1 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h1><p>  先介绍二维旋转变换,然后由此推广到三维绕坐标轴旋转,再推广得到绕三维任意轴旋转的罗德里格斯旋转公式。</p><h2 id="1-二维"><a href="#1-二维" class="headerlink" title="1.二维"></a>1.二维</h2><p>  如果要求$P$逆时针旋转$\theta$得到的$P^{‘}$,记$Q$为$P$逆时针旋转90°后的向量,即$(-P_y,P_x)$,则$P$与$Q$正好组成了<strong>该平面内的一组正交向量</strong>,任何向量都可由其线性表出,由基本几何和三角学可得到:</p><script type="math/tex; mode=display">P^{'}=Pcos\theta+Qsin\theta</script><p>则</p><script type="math/tex; mode=display">\begin{aligned}P_x^{'}&=P_xcos\theta-P_ysin\theta\\P_{y}^{'}&=P_ysin\theta-P_xcos\theta\\\end{aligned}</script><p>写成矩阵为</p><script type="math/tex; mode=display">P^{'}=\begin{bmatrix}cos\theta&-sin\theta\\sin\theta&cos\theta\end{bmatrix}P</script><h2 id="2-三维绕坐标轴旋转"><a href="#2-三维绕坐标轴旋转" class="headerlink" title="2.三维绕坐标轴旋转"></a>2.三维绕坐标轴旋转</h2><img src="/2020/06/06/2020-06-06-%E6%97%8B%E8%BD%AC%E5%8F%98%E6%8D%A2%E7%9F%A9%E9%98%B5%E6%8E%A8%E5%AF%BC/image-20200606172922405.png" class=""><p>  注意$R_y(\theta)$与其他不同是因为如果按照逆时针旋转,由$x\times z=-y$,则得到$y$轴负方向,因此需改为顺时针,即$-\theta$,这无论是在左手坐标系还是右手系都是相同的。</p><h2 id="3-三维绕任意轴旋转"><a href="#3-三维绕任意轴旋转" class="headerlink" title="3. 三维绕任意轴旋转"></a>3. 三维绕任意轴旋转</h2><p><strong>向量$P$绕任意轴$A$旋转$\theta$角度证明:</strong></p><p>  不妨设$A$为单位向量,同时$P$可分解为与$A$平行和垂直的两个分量,分别为:</p><script type="math/tex; mode=display">\begin{align}P_{projA}&=(A\cdot P)A=AA^TP\\P_{perpA}&=P-(A\cdot P)A=P-AA^TP\end{align}</script><p>如图所示</p><img src="/2020/06/06/2020-06-06-%E6%97%8B%E8%BD%AC%E5%8F%98%E6%8D%A2%E7%9F%A9%E9%98%B5%E6%8E%A8%E5%AF%BC/image-20200606161620614.png" class=""><p>故最终结果</p><script type="math/tex; mode=display">P^{'}=P_{perpA}^{'}+P_{ProjA}</script><p>其中<script type="math/tex">P_{PerpA}^{'}</script>为<script type="math/tex">P_{perpA}</script>旋转<script type="math/tex">\theta</script>后得到的向量,如图</p><img src="/2020/06/06/2020-06-06-%E6%97%8B%E8%BD%AC%E5%8F%98%E6%8D%A2%E7%9F%A9%E9%98%B5%E6%8E%A8%E5%AF%BC/image-20200606161747676.png" class=""><p>为求得其值,需找到一组线性组合来表示它,可选<script type="math/tex">P_{perpA}</script>与其旋转90°后的向量这两个向量作为组合,可以得知<script type="math/tex">A\times P</script>即为该向量,同时它的长度是与<script type="math/tex">P_{perpA}</script>相等的,这是因为</p><script type="math/tex; mode=display">|A\times P|=|P|sin\alpha=|P-(A\cdot P)A|</script><p>所以</p><script type="math/tex; mode=display">P_{perpA}^{'}=[P-(A\cdot P)A]cos\theta+(A \times P)sin \theta</script><p>所以</p><script type="math/tex; mode=display">\begin{align}P^{'}&=[P-(A\cdot P)A]cos\theta+(A \times P)sin \theta+(A\cdot P)A\\&=Pcos\theta+A\times P sin\theta+(A\cdot P)A(1-cos\theta)\end{align}</script><p>记$I$为单位阵,则矩阵形式为</p><script type="math/tex; mode=display">P^{'}=\{Icos\theta+\begin{bmatrix} 0&-A_z&A_y\\A_z&0&-A_x\\-A_y&A_x&0\end{bmatrix}sin\theta+AA^T(1-cos\theta)\}P</script><p>其中记</p><script type="math/tex; mode=display">R=Icos\theta+\begin{bmatrix} 0&-A_z&A_y\\A_z&0&-A_x\\-A_y&A_x&0\end{bmatrix}sin\theta+AA^T(1-cos\theta)</script><p>为罗德里格斯旋转公式,而将其展开写成一个矩阵就可得到旋转矩阵。</p>]]></content>
<categories>
<category>算法</category>
</categories>
<tags>
<tag>算法</tag>
</tags>
</entry>
<entry>
<title>Lagrange插值法与Newton插值法</title>
<link href="/2020/05/30/Lagrange%E6%8F%92%E5%80%BC%E6%B3%95%E4%B8%8ENewton%E6%8F%92%E5%80%BC%E6%B3%95/"/>
<url>/2020/05/30/Lagrange%E6%8F%92%E5%80%BC%E6%B3%95%E4%B8%8ENewton%E6%8F%92%E5%80%BC%E6%B3%95/</url>
<content type="html"><![CDATA[<h2 id="Lagrange插值法"><a href="#Lagrange插值法" class="headerlink" title="Lagrange插值法"></a>Lagrange插值法</h2><p> 考虑有$n$个<strong>不同的点</strong> <script type="math/tex">(x_1,y_1),(x_2,y_2),(x_i,y_i)\dots (x_n,y_n)</script> , 定义函数<script type="math/tex">f_i(x)</script>满足在<script type="math/tex">l_i(x_j)</script>满足克罗内克符号函数 <script type="math/tex">\delta_{ij}</script>,</p><script type="math/tex; mode=display">l_i(x_j)=\delta_{ij}=\begin{cases}1\quad\quad\quad i=j\\0\quad\quad\quad i\not=j\end{cases}</script><p>此时,若另</p><script type="math/tex; mode=display">L(x)=\sum_{i=1}^n y_il_i(x)</script><p>则对于任意$x_i$有</p><script type="math/tex; mode=display">y_i=l_i(x_i)</script><p>即$n$个点必然经过$L(x)$,即为所求。</p><p> 再考虑$l_i(x)$,其应为一个<strong>$n-1$次多项式</strong>,则可由因式法写出满足要求的函数:</p><script type="math/tex; mode=display">\begin{align}l_i(x)&=\frac{(x-x_1)(x-x_2)(x-x_3)\dots(x-x_{i-1})(x-x_{i+1})\dots(x_i-x_n)}{(x_i-x_1)(x_i-x_2)(x_i-x_3)\dots(x_i-x_{i-1})(x_i-x_{i+1})\dots(x_i-x_n)}\\\\&=\prod_{j\not=i}^{ 1\le j\le n }\frac{x-x_j}{x_i-x_j}\end{align}</script><p>这里的$l_i(x)$称为插值基函数。</p><p> 以下是代码实现</p><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><stdio.h></span></span><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><stdlib.h></span></span><span class="hljs-type">float</span> x[<span class="hljs-number">7</span>] = {<span class="hljs-number">1.20</span>, <span class="hljs-number">1.24</span>, <span class="hljs-number">1.28</span>, <span class="hljs-number">1.32</span>, <span class="hljs-number">1.36</span>, <span class="hljs-number">1.40</span>};<span class="hljs-type">float</span> y1[<span class="hljs-number">7</span>] = {<span class="hljs-number">1.09545</span>, <span class="hljs-number">1.11355</span>, <span class="hljs-number">1.13137</span>,<span class="hljs-number">1.14891</span>, <span class="hljs-number">1.16619</span>, <span class="hljs-number">1.18322</span>};<span class="hljs-type">float</span> y2[<span class="hljs-number">7</span>] = {<span class="hljs-number">0.07918</span>, <span class="hljs-number">0.09342</span>, <span class="hljs-number">0.10721</span>, <span class="hljs-number">0.12057</span>, <span class="hljs-number">0.13354</span>, <span class="hljs-number">0.14613</span>};<span class="hljs-type">float</span> xi[<span class="hljs-number">6</span>] = {<span class="hljs-number">1.22</span>, <span class="hljs-number">1.26</span>, <span class="hljs-number">1.30</span>, <span class="hljs-number">1.34</span>, <span class="hljs-number">1.38</span>};<span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">Lagrange</span><span class="hljs-params">(<span class="hljs-type">float</span> *y,<span class="hljs-type">float</span> cx)</span></span><span class="hljs-function"></span>{ <span class="hljs-type">int</span> n=<span class="hljs-number">6</span>; <span class="hljs-type">float</span> temp[<span class="hljs-number">10</span>],ans=<span class="hljs-number">0</span>; <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>;i<n;i++) { temp[i] = y[i]; <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> j=<span class="hljs-number">0</span>;j<n;j++) <span class="hljs-keyword">if</span>(j!=i) temp[i] *= (cx - x[j]) / (x[i] - x[j]); ans += temp[i]; } <span class="hljs-keyword">return</span> ans;}<span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>{ <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>;i++) <span class="hljs-built_in">printf</span>(<span class="hljs-string">"当x=%.2f,y1=%.5f,y2=%.5f\n"</span>, xi[i], <span class="hljs-built_in">Lagrange</span>(y1,xi[i]),<span class="hljs-built_in">Lagrange</span>(y2,xi[i])); <span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>); <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><p> 我们可以发现假如新加入一个点,就必须得重新计算,这是它的缺点.</p><h2 id="Newton插值法"><a href="#Newton插值法" class="headerlink" title="Newton插值法"></a>Newton插值法</h2><p> 该方法确定了一组新的基函数,确保能加入新的点能够重用之前的计算结果:</p><script type="math/tex; mode=display">\begin{aligned} \phi_{1}(x) &=1 \\\phi_{2}(x) &=\left(x-x_{1}\right) \\\phi_{3}(x) &=\left(x-x_{1}\right)\left(x-x_{2}\right) \\\cdots &=\cdots \\\phi_{n+1}(x) &=\prod_{i=1}^{n}\left(x-x_{i}\right)\end{aligned}</script><p>可以看到由于<script type="math/tex">\phi_{n+1}(x)=\phi_{n}(x)(x-x_{n})</script>,因此可以重用之前的结果。</p><p>则最终的多项式为:</p><script type="math/tex; mode=display">N(x)=\sum_{i=1}^{n+1}a_i\phi_i(x)</script><p>现在仅仅需要确定$a_i$的值就可以确定$N(x)$。</p><p> 我们将每个点依次带入相减可得到一个神奇的规律:</p><script type="math/tex; mode=display">\begin{aligned}a_1&=y_1\\a_2&=\frac{y_2-y_1}{x_2-x_1}\\a_3&=\frac{\frac{y_3-y_2}{x_3-x_2}-\frac{y_2-y_1}{x_2-x_1}}{x_3-x_1}\\\vdots\end{aligned}</script><p>我们把这种叫做<strong>差商</strong>,0阶均差定义为$f[x_i]=f(x_i)$,$n-1$阶差商为:</p><script type="math/tex; mode=display">f[x_1,x_2,x_3\dots x_n]=\frac{f[x_1,x_2,x_3\dots x_{n-1}]-f[x_2,x_3,x_4\dots x_n]}{x_n-x_1}</script><p>下面是差商表,每次迭代也可重用上一步的结果</p><p>所以最终为</p><script type="math/tex; mode=display">\begin{aligned}N(x)=& f\left(x_{1}\right)+\\& f\left[x_{1}, x_{2}\right]\left(x-x_{1}\right)+\\& f\left[x_{1}, x_{2}, x_{3}\right]\left(x-x_{1}\right)\left(x-x_{2}\right)+\\& f\left[x_{1}, \ldots, x_{4}\right]\left(x-x_{1}\right)\left(x-x_{2}\right)\left(x-x_{3}\right)+\\& \ldots\end{aligned}</script><p>以下是代码,可想而知,虽然Newton插值效率提高了,但是也要多出一部分来计算差商</p><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><iostream></span></span><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><stdio.h></span></span><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><stdlib.h></span></span><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<span class="hljs-type">float</span> x[<span class="hljs-number">7</span>] = {<span class="hljs-number">1.20</span>, <span class="hljs-number">1.24</span>, <span class="hljs-number">1.28</span>, <span class="hljs-number">1.32</span>, <span class="hljs-number">1.36</span>, <span class="hljs-number">1.40</span>};<span class="hljs-type">float</span> y1[<span class="hljs-number">7</span>] = {<span class="hljs-number">1.09545</span>, <span class="hljs-number">1.11355</span>, <span class="hljs-number">1.13137</span>,<span class="hljs-number">1.14891</span>, <span class="hljs-number">1.16619</span>, <span class="hljs-number">1.18322</span>};<span class="hljs-type">float</span> y2[<span class="hljs-number">7</span>] = {<span class="hljs-number">0.07918</span>, <span class="hljs-number">0.09342</span>, <span class="hljs-number">0.10721</span>, <span class="hljs-number">0.12057</span>, <span class="hljs-number">0.13354</span>, <span class="hljs-number">0.14613</span>};<span class="hljs-type">float</span> xi[<span class="hljs-number">6</span>] = {<span class="hljs-number">1.22</span>, <span class="hljs-number">1.26</span>, <span class="hljs-number">1.30</span>, <span class="hljs-number">1.34</span>, <span class="hljs-number">1.38</span>};<span class="hljs-type">float</span> diff[<span class="hljs-number">10</span>][<span class="hljs-number">10</span>];<span class="hljs-comment">//差商值表</span><span class="hljs-type">float</span> phi[<span class="hljs-number">10</span>];<span class="hljs-comment">//基函数值</span><span class="hljs-type">int</span> n=<span class="hljs-number">6</span>;<span class="hljs-comment">//求差商表,有点像动态规划</span><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">DifferenceQuotient</span><span class="hljs-params">(<span class="hljs-type">float</span> *y)</span></span><span class="hljs-function"></span>{ <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < n;i++) diff[i][<span class="hljs-number">0</span>] = y[i]; <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>; i < n;i++) { <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">1</span> ; j < i+<span class="hljs-number">1</span>;j++) diff[i][j] = (diff[i][j - <span class="hljs-number">1</span>] - diff[i<span class="hljs-number">-1</span>][j - <span class="hljs-number">1</span>]) / (x[i] - x[i-j]); }}<span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">Newton</span><span class="hljs-params">(<span class="hljs-type">float</span> *y,<span class="hljs-type">float</span> cx)</span></span><span class="hljs-function"></span>{ phi[<span class="hljs-number">0</span>] = <span class="hljs-number">1</span>; <span class="hljs-type">float</span> ans = <span class="hljs-number">0</span>; <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>; i < n;i++) <span class="hljs-comment">//其基函数的值</span> phi[i] = phi[i<span class="hljs-number">-1</span>]*(cx - x[i<span class="hljs-number">-1</span>]); <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < n;i++) ans += phi[i] * diff[i][i];<span class="hljs-comment">//多项式求和为最终结果</span> <span class="hljs-keyword">return</span> ans;}<span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>{ <span class="hljs-built_in">DifferenceQuotient</span>(y1); <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>;i++) <span class="hljs-built_in">printf</span>(<span class="hljs-string">"当x=%.2f,y1=%.5f\n"</span>, xi[i], <span class="hljs-built_in">Newton</span>(y1,xi[i])); cout << endl; <span class="hljs-built_in">DifferenceQuotient</span>(y2); <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>;i++) <span class="hljs-built_in">printf</span>(<span class="hljs-string">"当x=%.2f,y2=%.5f\n"</span>, xi[i],<span class="hljs-built_in">Newton</span>(y2,xi[i])); <span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);}</code></pre><p> 如果要新增节点,可以增量更新差商表</p><h2 id="两者关系"><a href="#两者关系" class="headerlink" title="两者关系"></a>两者关系</h2><p> 其实我们可以发现两个方法都是通过$n$个点确定了一组$n$个方程的方程组:</p><script type="math/tex; mode=display">\begin{cases}y_1&=a_0+a_1x_1+a_2x_1^2+\dots+a_nx_1^n\\y_2&=a_0+a_1x_2+a_2x_2^2+\dots+a_nx_2^n\\\vdots\\y_n&=a_0+a_1x_n+a_2x_n^2+\dots+a_nx_n^n\\\end{cases}</script><p>矩阵形式为$Y=XA$</p><script type="math/tex; mode=display">\begin{bmatrix} y_1\\y_2\\ \vdots \\y_n\end{bmatrix}=\begin{bmatrix} 1&x_1&x_1^2&\dots&x_1^n\\1&x_2&x_2^2&\dots&x_2^n\\ \vdots&\vdots&\vdots&\dots&\vdots\\1&x_n&x_n^2&\dots&x_n^n\end{bmatrix}\begin{bmatrix} a_0\\a_1\\ \vdots \\a_n\end{bmatrix}</script><p>系数矩阵$X$为范德蒙行列式,则$|X|\not =0$,因此可以得出其解$A$唯一,故最终确定的多项式唯一,即两者等效.Largrane法较为简单,而Newton法在需要新增节点时可以保持很好的效率。</p>]]></content>
<categories>
<category>算法</category>
</categories>
<tags>
<tag>算法</tag>
</tags>
</entry>
<entry>
<title>打家劫舍</title>
<link href="/2020/05/29/%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8D/"/>
<url>/2020/05/29/%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8D/</url>
<content type="html"><![CDATA[<h2 id="LeetCode-198-打家劫舍"><a href="#LeetCode-198-打家劫舍" class="headerlink" title="LeetCode 198 打家劫舍"></a>LeetCode 198 <a href="https://leetcode-cn.com/problems/house-robber/">打家劫舍</a></h2><p> 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。</p><p>给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。</p><pre><code class="hljs plaintext">输入: [2,7,9,3,1]输出: 12解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 偷窃到的最高金额 = 2 + 9 + 1 = 12 。</code></pre><p> 动态规划,自然而然想到每个房子有两种状态偷与不偷:偷的话只能是最走到<strong>前前一个房子是偷盗的金额总和</strong>加上当前房子金额,不偷的话就是等于走到<strong>前一个房子时偷到的金额总和</strong>,定义$dp[i]$为$nums[0…i]$的能偷到的最高金额,则转移方程为</p><script type="math/tex; mode=display">dp[i]=\begin{cases} max(dp[i-1],dp[i-2]+nums[i]) \quad\quad\quad\quad\quad &i\in[ 2,j]\\ nums[0] &i=0\\ max(nums[0],nums[1]) &i=1\end{cases}</script><pre><code class="hljs C++"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> {<span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">rob</span><span class="hljs-params">(vector<<span class="hljs-type">int</span>>& nums)</span> </span>{ <span class="hljs-keyword">if</span>(nums.<span class="hljs-built_in">size</span>()==<span class="hljs-number">0</span>)<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; <span class="hljs-keyword">if</span>(nums.<span class="hljs-built_in">size</span>()==<span class="hljs-number">1</span>)<span class="hljs-keyword">return</span> nums[<span class="hljs-number">0</span>];<span class="hljs-comment">//边界情况</span> <span class="hljs-type">int</span> dp[<span class="hljs-number">1000</span>]={<span class="hljs-number">0</span>}; dp[<span class="hljs-number">0</span>]=nums[<span class="hljs-number">0</span>]; dp[<span class="hljs-number">1</span>]=<span class="hljs-built_in">max</span>(nums[<span class="hljs-number">0</span>],nums[<span class="hljs-number">1</span>]); <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i=<span class="hljs-number">2</span>;i<nums.<span class="hljs-built_in">size</span>();i++) { dp[i]=<span class="hljs-built_in">max</span>(dp[i<span class="hljs-number">-2</span>]+nums[i],dp[i<span class="hljs-number">-1</span>]); } <span class="hljs-keyword">return</span> dp[nums.<span class="hljs-built_in">size</span>()<span class="hljs-number">-1</span>]; }};</code></pre>]]></content>
<categories>
<category>习题</category>
</categories>
<tags>
<tag>习题</tag>
</tags>
</entry>
<entry>
<title>LeetCodeのTree</title>
<link href="/2020/05/26/Tree/"/>
<url>/2020/05/26/Tree/</url>
<content type="html"><![CDATA[<h1 id="关于树"><a href="#关于树" class="headerlink" title="关于树"></a>关于树</h1><h2 id="简单"><a href="#简单" class="headerlink" title="简单"></a>简单</h2><h3 id="100-相同的树"><a href="#100-相同的树" class="headerlink" title="100 相同的树"></a>100 <a href="https://leetcode-cn.com/problems/same-tree/">相同的树</a></h3><p>给定两个二叉树,编写一个函数来检验它们是否相同。</p><p>如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。</p><pre><code class="hljs c++"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> {<span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">isSameTree</span><span class="hljs-params">(TreeNode* p, TreeNode* q)</span> </span>{ <span class="hljs-keyword">if</span>(!p&&!q)<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; <span class="hljs-comment">//都为空返回true</span> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(p&&q&&p->val==q->val)<span class="hljs-comment">//相等往下做判断</span> <span class="hljs-keyword">return</span> <span class="hljs-built_in">isSameTree</span>(p->left,q->left)&&<span class="hljs-built_in">isSameTree</span>(p->right,q->right); <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>; }};</code></pre><h3 id="101-对称二叉树"><a href="#101-对称二叉树" class="headerlink" title="101 对称二叉树"></a>101 <a href="https://leetcode-cn.com/problems/symmetric-tree/">对称二叉树</a></h3><p>给定一个二叉树,检查它是否是镜像对称的。</p><p>上题的引申</p><pre><code class="hljs c++"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> {<span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">isSymmetric</span><span class="hljs-params">(TreeNode* root)</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-built_in">judge</span>(root,root); } <span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">judge</span><span class="hljs-params">(TreeNode* p,TreeNode* q)</span></span><span class="hljs-function"> </span>{ <span class="hljs-keyword">if</span>(!p&&!q)<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(p&&q&&p->val==q->val) <span class="hljs-keyword">return</span> <span class="hljs-built_in">judge</span>(p->left,q->right)&&<span class="hljs-built_in">judge</span>(p->right,q->left); <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>; }};</code></pre><h3 id="104-二叉树的最大深度"><a href="#104-二叉树的最大深度" class="headerlink" title="104 二叉树的最大深度"></a>104 <a href="https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/">二叉树的最大深度</a></h3><p>给定一个二叉树,找出其最大深度。</p><p>二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。</p><pre><code class="hljs c++"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> {<span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">maxDepth</span><span class="hljs-params">(TreeNode* root)</span> </span>{ <span class="hljs-keyword">if</span>(!root)<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; <span class="hljs-keyword">return</span> <span class="hljs-built_in">max</span>(<span class="hljs-built_in">maxDepth</span>(root->left),<span class="hljs-built_in">maxDepth</span>(root->right))+<span class="hljs-number">1</span>;<span class="hljs-comment">//返回左右最大值 </span> }};</code></pre><h2 id="中等"><a href="#中等" class="headerlink" title="中等"></a>中等</h2><h3 id="94-二叉树的中序遍历"><a href="#94-二叉树的中序遍历" class="headerlink" title="94 二叉树的中序遍历"></a>94 <a href="https://leetcode-cn.com/problems/binary-tree-inorder-traversal/">二叉树的中序遍历</a></h3><p>非递归方法</p><pre><code class="hljs c++"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> {<span class="hljs-keyword">public</span>: <span class="hljs-function">vector<<span class="hljs-type">int</span>> <span class="hljs-title">inorderTraversal</span><span class="hljs-params">(TreeNode* root)</span> </span>{ stack<TreeNode*>s; vector<<span class="hljs-type">int</span>> ans; <span class="hljs-keyword">while</span>(!s.<span class="hljs-built_in">empty</span>()||root) { <span class="hljs-keyword">if</span>(root) { s.<span class="hljs-built_in">push</span>(root); root=root->left; } <span class="hljs-keyword">else</span> { TreeNode* temp=s.<span class="hljs-built_in">top</span>(); s.<span class="hljs-built_in">pop</span>(); ans.<span class="hljs-built_in">push_back</span>(temp->val); root=temp->right; } } <span class="hljs-keyword">return</span> ans; }};</code></pre><h3 id="105-从前序与中序遍历序列构造二叉树"><a href="#105-从前序与中序遍历序列构造二叉树" class="headerlink" title="105 从前序与中序遍历序列构造二叉树"></a>105 <a href="https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/">从前序与中序遍历序列构造二叉树</a></h3><p>递归构造,常规。</p><pre><code class="hljs c++"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> {<span class="hljs-keyword">public</span>: <span class="hljs-function">TreeNode* <span class="hljs-title">build</span><span class="hljs-params">(vector<<span class="hljs-type">int</span>>& pre,<span class="hljs-type">int</span> ps,<span class="hljs-type">int</span> pe, vector<<span class="hljs-type">int</span>>& inorder,<span class="hljs-type">int</span> is,<span class="hljs-type">int</span> ie)</span></span><span class="hljs-function"> </span>{ <span class="hljs-keyword">if</span>(ps>pe||is>ie)<span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>; TreeNode* t=<span class="hljs-keyword">new</span> <span class="hljs-built_in">TreeNode</span>(); t->val=pre[ps]; <span class="hljs-type">int</span> k=is; <span class="hljs-keyword">while</span>(is<ie&&inorder[k]!=t->val)k++;<span class="hljs-comment">//寻找中序中的头节点</span> t->left=<span class="hljs-built_in">build</span>(pre,ps+<span class="hljs-number">1</span>,ps+k-is,inorder,is,k<span class="hljs-number">-1</span>);<span class="hljs-comment">//构造左子树</span> t->right=<span class="hljs-built_in">build</span>(pre,ps+k-is+<span class="hljs-number">1</span>,pe,inorder,k+<span class="hljs-number">1</span>,ie);<span class="hljs-comment">//构造右子树</span> <span class="hljs-keyword">return</span> t; } <span class="hljs-function">TreeNode* <span class="hljs-title">buildTree</span><span class="hljs-params">(vector<<span class="hljs-type">int</span>>& preorder, vector<<span class="hljs-type">int</span>>& inorder)</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-built_in">build</span>(preorder,<span class="hljs-number">0</span>,preorder.<span class="hljs-built_in">size</span>()<span class="hljs-number">-1</span>,inorder,<span class="hljs-number">0</span>,inorder.<span class="hljs-built_in">size</span>()<span class="hljs-number">-1</span>); }};</code></pre><h2 id="困难"><a href="#困难" class="headerlink" title="困难"></a>困难</h2>]]></content>
<categories>
<category>C++</category>
</categories>
<tags>
<tag>习题</tag>
</tags>
</entry>
<entry>
<title>通过可逆运算交换变量的值</title>
<link href="/2020/05/23/%E9%80%9A%E8%BF%87%E5%8F%AF%E9%80%86%E8%BF%90%E7%AE%97%E4%BA%A4%E6%8D%A2%E5%8F%98%E9%87%8F%E7%9A%84%E5%80%BC/"/>
<url>/2020/05/23/%E9%80%9A%E8%BF%87%E5%8F%AF%E9%80%86%E8%BF%90%E7%AE%97%E4%BA%A4%E6%8D%A2%E5%8F%98%E9%87%8F%E7%9A%84%E5%80%BC/</url>
<content type="html"><![CDATA[<h1 id="通过可逆运算交换变量的值"><a href="#通过可逆运算交换变量的值" class="headerlink" title="通过可逆运算交换变量的值"></a>通过可逆运算交换变量的值</h1><h3 id="1-介绍"><a href="#1-介绍" class="headerlink" title="1 介绍"></a>1 介绍</h3><p> 交换两个变量的值除了引入临时变量,还有通过加减或异或等奇淫技巧来完成,现在来介绍其原理并且推广它们。</p><h2 id="2-交换的原理"><a href="#2-交换的原理" class="headerlink" title="2 交换的原理"></a>2 交换的原理</h2><p> 加减、异或都是可逆运算,现在定义一种可逆运算规则$f$,它的逆运算是$f^{-1}$,不妨设$f(a,b)=c$,则有$f^{-1}(c,a)=b$,则有如下推导,注意我用<code>:=</code>来表示赋值:</p><script type="math/tex; mode=display">\begin{align}a& :=f(a,b) \tag{1}\label{1}\\b& :=f^{-1}(a,b)=f^{-1}(f(a,b),b)=a \tag{2}\label{2}\\a& :=f^{-1}(a,b)=f^{-1}(f(a,b),a)=b \tag{3}\label{3}\end{align}</script><p>可以看到,最终$a,b$交换了两者的值,因此可见只要是逆运算就可以满足要求,比如使用乘除:</p><pre><code class="hljs c++">a=a*b;b=a/b;a=a/b;</code></pre><p>我们可以代入数字验证一下,可以发现它也是正确的。</p><h2 id="3-交换律的影响"><a href="#3-交换律的影响" class="headerlink" title="3 交换律的影响"></a>3 交换律的影响</h2><p> 但是,如果你细心点,你会发现既然乘除可以,那为什么要先乘后除,或者干脆使用乘方与开方,因为它们也是一组逆运算,可是当我们这样操作会发现答案是与预期不符的。因为问题出在交换律上。</p><h3 id="3-1-f-与-f-1-只有一者满足交换律"><a href="#3-1-f-与-f-1-只有一者满足交换律" class="headerlink" title="3.1 $f$与$f^{-1}$只有一者满足交换律"></a>3.1 $f$与$f^{-1}$只有一者满足交换律</h3><p> 如果$f(a,b)\not= f(b,a)$,我们也就能得出$f^{-1}(c,a)\not=f^{-1}(c,b)$,而公式$\eqref{2}\eqref{3}$正是依赖于此的。也可这样想,不妨设$f(a,b)=c_1,f(b,a)=c_2$,则正确的应该是$f^{-1}(c_1,a)=b,f^{-1}(c_2,b)=a$。所以运算规则$f$应满足交换律,其逆运算则无要求。</p><p> 那么,如果$f$不满足交换律,可以实现交换变量而不引入临时变量吗?答案当然是可以的,不过这就对不同的$f$就有不同的方法了,因为我们要知道$c_1$与$c_2$的关系,如果$f$是减法,则$c_1=-c_2$;如果是除法,则$c_1=c_2^{-1}$。</p><h3 id="3-2-f-与-f-1-都不满足交换律"><a href="#3-2-f-与-f-1-都不满足交换律" class="headerlink" title="3.2 $f$与$f^{-1}$都不满足交换律"></a>3.2 $f$与$f^{-1}$都不满足交换律</h3><p> 这个条件下很好的例子就是矩阵和幂两种。矩阵涉及左乘、右乘、逆;幂涉及乘方、开方、对数。它们的运算时涉及到三种运算规则的。</p><p> 以矩阵为例:</p><script type="math/tex; mode=display">\begin{align}A&:=AB\\B&:=AB^{-1}=ABB^{-1}=A\\A&:=B^{-1}A=A^{-1}AB=B\\\end{align}</script><p> 幂运算:</p><p> </p><script type="math/tex; mode=display">\begin{align}a&:=a^b\\b&:=\sqrt[b]{a}=\sqrt[b]{a^b}=a\\a&:=\log_{a}{a}=\log_{a}{a^b}=b\end{align}</script><p>这种情况下是每种运算规则都是需要用到才能完成交换的。</p><h3 id="3-3-f-与-f-1-都满足交换律"><a href="#3-3-f-与-f-1-都满足交换律" class="headerlink" title="3.3 $f$与$f^{-1}$都满足交换律"></a>3.3 $f$与$f^{-1}$都满足交换律</h3><p> 这个的最好的例子就是异或运算$\oplus $,因为它除了自身就满足交换,同时它的逆运算就是自身$f=f^{-1}$,</p><script type="math/tex; mode=display">\begin{align}a&:=a\oplus b\\b&:=a\oplus b=a\oplus b\oplus b =a\\a&:=a\oplus b=a\oplus b\oplus a =b\end{align}</script><h3 id="4-总结"><a href="#4-总结" class="headerlink" title="4 总结"></a>4 总结</h3><p> 主要讲了讲这种变量交换操作的背后原理即推广,但是需要提醒的是在实际应用中,我还是建议通过临时变量来进行交换,上面的操作虽然给人眼前一亮,同时也有理论支持,可应用在实际中还是可能会出现错误,比如溢出,甚至交换变量类型根本就不是数字。我的建议是虽然这很有趣但请不要用。</p><p> </p>]]></content>
<categories>
<category>算法</category>
</categories>
<tags>
<tag>算法</tag>
</tags>
</entry>
<entry>
<title>Hello World</title>
<link href="/2020/05/23/hello-world/"/>
<url>/2020/05/23/hello-world/</url>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><pre><code class="hljs bash">$ hexo new <span class="hljs-string">"My New Post"</span></code></pre><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><pre><code class="hljs bash">$ hexo server</code></pre><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><pre><code class="hljs bash">$ hexo generate</code></pre><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><pre><code class="hljs bash">$ hexo deploy</code></pre><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
</entry>
<entry>
<title>Template类模板</title>
<link href="/2020/05/21/Template%E7%B1%BB%E6%A8%A1%E6%9D%BF/"/>
<url>/2020/05/21/Template%E7%B1%BB%E6%A8%A1%E6%9D%BF/</url>
<content type="html"><![CDATA[<h1 id="Template"><a href="#Template" class="headerlink" title="Template"></a>Template</h1><h2 id="1-函数模板"><a href="#1-函数模板" class="headerlink" title="1.函数模板"></a>1.函数模板</h2><p>编译期间编译器自动分析参数类型,<code>template</code>函数模板是支持默认参数的,<code>T1 、T2</code> 顺序在默认情况下是可以任意的,不用严格按照从右到左的顺序,例如:<code>template <typename T1 ,typename T2 = int></code></p><pre><code class="hljs cpp"><span class="hljs-comment">//函数模板化 </span><span class="hljs-function"><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T></span><span class="hljs-function">T <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-type">const</span> T lval,<span class="hljs-type">const</span> T rval)</span></span><span class="hljs-function"></span>{ T ans = lval + rval; <span class="hljs-keyword">return</span> ans; }</code></pre><pre><code class="hljs cpp">cout << <span class="hljs-built_in">add</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>) << endl;cout << <span class="hljs-built_in">add</span>(<span class="hljs-number">2.1</span>, <span class="hljs-number">1.2</span>) << endl;<span class="hljs-comment">//add(2, 1.2)会报错,因为变成了add(int,double)</span>输出: <span class="hljs-number">3</span> <span class="hljs-number">3.3</span></code></pre><h2 id="2-类模板"><a href="#2-类模板" class="headerlink" title="2.类模板"></a>2.类模板</h2><p>将模板思想应用于类,类模板也支持默认参数,但是类模板必须严格从右往左默认化。</p><pre><code class="hljs cpp"><span class="hljs-comment">//类模板</span><span class="hljs-keyword">template</span><<span class="hljs-keyword">class</span> <span class="hljs-title class_">T</span>><span class="hljs-keyword">class</span> <span class="hljs-title class_">Myclass</span>{ T a; <span class="hljs-keyword">public</span>: <span class="hljs-function">T <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-type">const</span> T lval, <span class="hljs-type">const</span> T rval)</span></span>;};<span class="hljs-keyword">template</span> <<span class="hljs-keyword">class</span> <span class="hljs-title class_">T</span>>T Myclass <T>::<span class="hljs-built_in">add</span>(<span class="hljs-type">const</span> T lval, <span class="hljs-type">const</span> T rval){ a = lval + rval; <span class="hljs-keyword">return</span> a;}</code></pre><pre><code class="hljs cpp">Myclass<<span class="hljs-type">double</span>> myclass;cout << myclass.<span class="hljs-built_in">add</span>(<span class="hljs-number">1.2</span>, <span class="hljs-number">3.3</span>) << endl;输出: <span class="hljs-number">4.5</span></code></pre><h2 id="3-成员模板"><a href="#3-成员模板" class="headerlink" title="3.成员模板"></a>3.成员模板</h2><p>在模板类内使用模板函数。</p><pre><code class="hljs cpp"><span class="hljs-comment">//成员模板</span><span class="hljs-keyword">template</span><<span class="hljs-keyword">class</span> <span class="hljs-title class_">T</span>><span class="hljs-keyword">class</span> <span class="hljs-title class_">Mylove</span>{ <span class="hljs-keyword">public</span>: T a; <span class="hljs-keyword">template</span> <<span class="hljs-keyword">typename</span> type_1, <span class="hljs-keyword">typename</span> type_2> <span class="hljs-function">type_1 <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-type">const</span> type_1 lval, <span class="hljs-type">const</span> type_2 rval)</span></span>;};<span class="hljs-keyword">template</span> <<span class="hljs-keyword">class</span> <span class="hljs-title class_">T</span>><span class="hljs-keyword">template</span> <<span class="hljs-keyword">typename</span> type_1, <span class="hljs-keyword">typename</span> type_2>type_1 Mylove<T>::<span class="hljs-built_in">add</span>(<span class="hljs-type">const</span> type_1 lval, <span class="hljs-type">const</span> type_2 rval){ a = lval + rval; <span class="hljs-keyword">return</span> a;}</code></pre><pre><code class="hljs cpp">Mylove<<span class="hljs-type">double</span>> mylove;cout << mylove.<span class="hljs-built_in">add</span>(<span class="hljs-number">0</span>, <span class="hljs-number">20.2</span>) << endl; <span class="hljs-comment">//答案为20,因为该函数返回为第一个参数相同,该过程为int+double=double,但最终返回int</span>输出:<span class="hljs-number">20</span></code></pre><h2 id="4-嵌套类模板"><a href="#4-嵌套类模板" class="headerlink" title="4.嵌套类模板"></a>4.嵌套类模板</h2><p>模板类内嵌套模板类。</p><pre><code class="hljs cpp"><span class="hljs-comment">//嵌套类模板</span><span class="hljs-keyword">template</span><<span class="hljs-keyword">class</span> <span class="hljs-title class_">T</span>><span class="hljs-keyword">class</span> <span class="hljs-title class_">Mylife</span>{ <span class="hljs-keyword">public</span>: T a; <span class="hljs-keyword">template</span> <<span class="hljs-keyword">class</span> <span class="hljs-title class_">type_3</span>> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Mystory</span>;<span class="hljs-comment">//先声明,在外部定义</span> Mystory<T> mystory;<span class="hljs-comment">//用T实例化</span>};<span class="hljs-keyword">template</span><<span class="hljs-keyword">class</span> <span class="hljs-title class_">T</span>> <span class="hljs-keyword">template</span><<span class="hljs-keyword">class</span> <span class="hljs-title class_">type_3</span>> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Mylife</span><T>::Mystory { <span class="hljs-keyword">public</span>: type_3 c; <span class="hljs-function">type_3 <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-type">const</span> type_3 lval, <span class="hljs-type">const</span> type_3 rval)</span> </span><span class="hljs-function"> </span>{ c = lval + rval; <span class="hljs-keyword">return</span> c; }; };</code></pre><pre><code class="hljs cpp">Mylife<<span class="hljs-type">double</span>> mylife;cout << mylife.mystory.<span class="hljs-built_in">add</span>(<span class="hljs-number">1</span>, <span class="hljs-number">1.1</span>) << endl;输出: <span class="hljs-number">2.1</span></code></pre><h2 id="5-总结"><a href="#5-总结" class="headerlink" title="5.总结"></a>5.总结</h2><p>使用<code>template</code>,以不变应万变,平常使用的STL正是基于此。</p>]]></content>
<categories>
<category>C++</category>
</categories>
<tags>
<tag>C++</tag>
</tags>
</entry>
<entry>
<title>Unity下计步器Pedometer算法的实现</title>
<link href="/2020/05/15/Unity%E4%B8%8B%E8%AE%A1%E6%AD%A5%E5%99%A8Pedometer%E7%AE%97%E6%B3%95%E7%9A%84%E5%AE%9E%E7%8E%B0/"/>
<url>/2020/05/15/Unity%E4%B8%8B%E8%AE%A1%E6%AD%A5%E5%99%A8Pedometer%E7%AE%97%E6%B3%95%E7%9A%84%E5%AE%9E%E7%8E%B0/</url>
<content type="html"><![CDATA[<h2 id="Unity下计步器Pedometer的实现"><a href="#Unity下计步器Pedometer的实现" class="headerlink" title="Unity下计步器Pedometer的实现"></a>Unity下计步器Pedometer的实现</h2><p><strong>1.环境</strong><br>unity2018,Android(小米5)<br><strong>2.原理</strong><br><a href="https://blog.csdn.net/qq_36490364/article/details/104522980">加速度传感器的计步算法Pedometer</a><br><strong>3.实现</strong><br>注释写的很清楚了应该<br><pre><code class="hljs c#"><span class="hljs-keyword">using</span> UnityEngine;<span class="hljs-keyword">using</span> UnityEngine.Networking;<span class="hljs-keyword">using</span> UnityEngine.UI;<span class="hljs-meta">#<span class="hljs-keyword">pragma</span> <span class="hljs-keyword">warning</span> disable CS0618</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">pedometer</span> : <span class="hljs-title">MonoBehaviour</span>{ <span class="hljs-keyword">public</span> Text statusText,stepsText; <span class="hljs-keyword">public</span> <span class="hljs-built_in">float</span> lowLimit = <span class="hljs-number">0.005f</span>; <span class="hljs-comment">//平缓</span> <span class="hljs-keyword">public</span> <span class="hljs-built_in">float</span> highLimit = <span class="hljs-number">0.1f</span>; <span class="hljs-comment">// 走路时的波峰波谷</span> <span class="hljs-keyword">public</span> <span class="hljs-built_in">float</span> vertHighLimit = <span class="hljs-number">0.25f</span>;<span class="hljs-comment">//跳跃时的波峰波谷</span> <span class="hljs-keyword">private</span> <span class="hljs-built_in">bool</span> isHigh = <span class="hljs-literal">false</span>; <span class="hljs-comment">// 状态</span> <span class="hljs-keyword">private</span> <span class="hljs-built_in">float</span> filterCurrent = <span class="hljs-number">10.0f</span>; <span class="hljs-comment">// 滤波参数 得到拟合值</span> <span class="hljs-keyword">private</span> <span class="hljs-built_in">float</span> filterAverage = <span class="hljs-number">0.1f</span>; <span class="hljs-comment">// 滤波参数 得到均值</span> <span class="hljs-keyword">private</span> <span class="hljs-built_in">float</span> accelerationCurrent = <span class="hljs-number">0f</span>; <span class="hljs-comment">//拟合值</span> <span class="hljs-keyword">private</span> <span class="hljs-built_in">float</span> accelerationAverage = <span class="hljs-number">0f</span>;<span class="hljs-comment">//均值</span> <span class="hljs-keyword">private</span> <span class="hljs-built_in">int</span> steps = <span class="hljs-number">0</span>; <span class="hljs-comment">// 步数</span> <span class="hljs-keyword">private</span> <span class="hljs-built_in">int</span> oldSteps; <span class="hljs-keyword">private</span> <span class="hljs-built_in">float</span> deltaTime = <span class="hljs-number">0f</span>;<span class="hljs-comment">//计时器</span> <span class="hljs-keyword">private</span> <span class="hljs-built_in">int</span> jumpCount = <span class="hljs-number">0</span>;<span class="hljs-comment">//跳跃数</span> <span class="hljs-keyword">private</span> <span class="hljs-built_in">int</span> oldjumpCount = <span class="hljs-number">0</span>; <span class="hljs-keyword">private</span> <span class="hljs-built_in">bool</span> startTimer = <span class="hljs-literal">false</span>;<span class="hljs-comment">//开始计时</span> <span class="hljs-keyword">private</span> <span class="hljs-built_in">bool</span> isWalking = <span class="hljs-literal">false</span>; <span class="hljs-keyword">private</span> <span class="hljs-built_in">bool</span> isJumping = <span class="hljs-literal">false</span>; <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Awake</span>()</span> { accelerationAverage = Input.acceleration.magnitude; oldSteps = steps; oldjumpCount =jumpCount ; } <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Update</span>()</span> { checkWalkingAndJumping(); <span class="hljs-comment">// 检测是否行走</span> <span class="hljs-keyword">if</span> (isWalking) { statusText.text = (<span class="hljs-string">"状态:行走"</span>); } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!isWalking) { statusText.text = (<span class="hljs-string">"状态:不动"</span>); } <span class="hljs-keyword">if</span> (isJumping) { statusText.text = (<span class="hljs-string">"状态:跳跃"</span>); } } <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">FixedUpdate</span>()</span> { <span class="hljs-comment">//通过Lerp对Input.acceleration.magnitude(加速度标量和)滤波</span> <span class="hljs-comment">//这里使用线性插值的公式正好为EMA一次指数滤波 y[i]=y[i-1]+(x[i]-y[i])*k=(1-k)*y[i]+kx[i]</span> accelerationCurrent = Mathf.Lerp(accelerationCurrent, Input.acceleration.magnitude, Time.deltaTime * filterCurrent); accelerationAverage = Mathf.Lerp(accelerationAverage, Input.acceleration.magnitude, Time.deltaTime * filterAverage); <span class="hljs-built_in">float</span> delta = accelerationCurrent - accelerationAverage; <span class="hljs-comment">// 获取差值,即坡度</span> <span class="hljs-keyword">if</span> (!isHigh) { <span class="hljs-keyword">if</span> (delta > highLimit)<span class="hljs-comment">//往高</span> { isHigh = <span class="hljs-literal">true</span>; steps++; stepsText.text = <span class="hljs-string">"步数: "</span> + steps+<span class="hljs-string">"\n跳跃数:"</span>+jumpCount; } <span class="hljs-keyword">if</span>(delta>vertHighLimit) { isHigh = <span class="hljs-literal">true</span>; jumpCount++; stepsText.text = <span class="hljs-string">"步数: "</span> + steps + <span class="hljs-string">"\n跳跃数:"</span> + jumpCount; } } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">if</span> (delta < lowLimit)<span class="hljs-comment">//往低</span> { isHigh = <span class="hljs-literal">false</span>; } } } <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">checkWalkingAndJumping</span>()</span> { <span class="hljs-keyword">if</span> ((steps != oldSteps)||(oldjumpCount != jumpCount)) { startTimer = <span class="hljs-literal">true</span>; deltaTime = <span class="hljs-number">0f</span>; } <span class="hljs-keyword">if</span> (startTimer)<span class="hljs-comment">//计时器,让更新UI的慢一点,因为你不可能走路只用一帧的时间QAQ</span> { deltaTime += Time.deltaTime; <span class="hljs-keyword">if</span> (deltaTime != <span class="hljs-number">0</span>) { <span class="hljs-keyword">if</span> (oldjumpCount != jumpCount)<span class="hljs-comment">//检测是否是跳跃</span> isJumping = <span class="hljs-literal">true</span>; <span class="hljs-keyword">else</span> isWalking = <span class="hljs-literal">true</span>; } <span class="hljs-keyword">if</span> (deltaTime > <span class="hljs-number">2</span>) { deltaTime = <span class="hljs-number">0F</span>; startTimer = <span class="hljs-literal">false</span>; } } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!startTimer) { isWalking = <span class="hljs-literal">false</span>; isJumping = <span class="hljs-literal">false</span>; } oldSteps = steps; oldjumpCount = jumpCount; }}</code></pre></p>]]></content>
<categories>
<category>Unity</category>
<category>算法</category>
</categories>
<tags>
<tag>算法</tag>
<tag>Unity</tag>
</tags>
</entry>
<entry>
<title>加速度传感器的计步算法Pedometer</title>
<link href="/2020/05/15/%E5%8A%A0%E9%80%9F%E5%BA%A6%E4%BC%A0%E6%84%9F%E5%99%A8%E7%9A%84%E8%AE%A1%E6%AD%A5%E7%AE%97%E6%B3%95Pedometer/"/>
<url>/2020/05/15/%E5%8A%A0%E9%80%9F%E5%BA%A6%E4%BC%A0%E6%84%9F%E5%99%A8%E7%9A%84%E8%AE%A1%E6%AD%A5%E7%AE%97%E6%B3%95Pedometer/</url>
<content type="html"><![CDATA[<h2 id="加速度传感器的计步算法Pedometer"><a href="#加速度传感器的计步算法Pedometer" class="headerlink" title="加速度传感器的计步算法Pedometer"></a>加速度传感器的计步算法Pedometer</h2><p><strong>1.前言</strong><br>  最近要使用到计步,查了很多资料,以下是一个简易的计步算法,也能检测跳跃</p><p><strong>2.工具</strong><br>  <a href="https://download.csdn.net/download/qq_36490364/12193880">一个实时绘制Android加速度的APP</a></p><p><strong>3.内容</strong></p><ol><li><p>Android的加速传感器的介绍</p><p>  Android上的加速度传感器的方向如下所示(当你面朝手机屏幕观察),这是一个右手坐标系。因此加速度是由三个正交的向量组成的,例如当你平放手机在桌子上屏幕朝上,此时应为(0,0,9.6左右)<img src="/2020/05/15/%E5%8A%A0%E9%80%9F%E5%BA%A6%E4%BC%A0%E6%84%9F%E5%99%A8%E7%9A%84%E8%AE%A1%E6%AD%A5%E7%AE%97%E6%B3%95Pedometer/image-20200524015251937.png" class="" title="image-20200524015251937"></p></li></ol><ol><li>使用g-sensor对数据进行分析<br>   打开这个工具后进行走路和跳跃,然后导出数据,以时间为横轴画出折线图<img src="/2020/05/15/%E5%8A%A0%E9%80%9F%E5%BA%A6%E4%BC%A0%E6%84%9F%E5%99%A8%E7%9A%84%E8%AE%A1%E6%AD%A5%E7%AE%97%E6%B3%95Pedometer/20200226221501436-1590254598388.png" class="" title="在这里插入图片描述"><br> 我们多实验几组实验后会发现,当手机以不同的姿态放入口袋然后进行运动时,xyz三轴的数据是不确定的,但是三者矢量和的模却是有规律的(其实真正的加速度被正交分解了),因此我们接下来应该分析的是它们的模|V|<br> <img src="/2020/05/15/%E5%8A%A0%E9%80%9F%E5%BA%A6%E4%BC%A0%E6%84%9F%E5%99%A8%E7%9A%84%E8%AE%A1%E6%AD%A5%E7%AE%97%E6%B3%95Pedometer/202002262042493.png" class="" title="在这里插入图片描述">  上图中可以知道,每次走路都会产生一个波峰和波谷,而跳跃的振幅相对大一些。因此判断行走只需判断波峰波谷就行</li></ol><ol><li>数据处理<br>  不妨记第$i$个时间单位获取到的加速度的三个矢量和的模大小为$x_i$,为了使数据更加平滑,分别使用两次的指数滤波(EMA)<script type="math/tex; mode=display">\left\{\begin{aligned}a_{i}&=(1-p_1)a_{i-1}+p_{1}x_{i}\\b_{i}&=(1-p_2)b_{i-1}+p_{2}x_{i}\\a_{0}&=b_{0}=x_{0}\\p_{1}&>p_{2}\\\end{aligned}\right.</script></li></ol><p>  其中$a_i$是拟合曲线,$b_i$均值,因此${\Delta{h}=a_i-b_i}$即波峰波谷的高度差,得到${\Delta{h}}$后再跟设置好的${k_1,k_2,k_3}$比较(其中${k_1>k_2>k_3}$)。判别结果如下,</p><script type="math/tex; mode=display">\left\{\begin{aligned}&jump,\Delta{h}>k_1\\&walking,\Delta{h}>k_2\\&standing,\Delta{h}<k_3\\\end{aligned}\right.</script><p>  需要注意的是.一旦检测到跳跃或者行走,就得有计时器计时,当经过$\Delta t$后再进行判别,因为人走路和跳跃是需要时间的!!!</p><p> 其中$p_1$,$p_2$,$k_1$,$k_2$,$k_3$,$\Delta t$ 都是需要我们设置的参数,下面分享一组<strong>手调</strong>测出来的参数QAQ,不一定最优但能行。。。</p><script type="math/tex; mode=display">\left\{\begin{aligned}p_1&=0.2\\p_2&=0.002\\k_1&=0.25\\k_2&=0.13\\k_3&=0.008\\\Delta t&=2s\end{aligned}\right.</script><img src="/2020/05/15/%E5%8A%A0%E9%80%9F%E5%BA%A6%E4%BC%A0%E6%84%9F%E5%99%A8%E7%9A%84%E8%AE%A1%E6%AD%A5%E7%AE%97%E6%B3%95Pedometer/20200227144604718.png" class="" title="在这里插入图片描述"><p>  上图是带入这些参数得到的曲线,我们可以看到经过$p_1$参数的滤波得到的曲线(红色),已经大致拟合原数据(蓝色),而经过$p_2$参数得到的曲线(绿色)大致为均值,要说一下的是其实绿线的值大致在9.8附近,就是重力加速度,为什么不直接采用9.8的原因是实际会有误差。</p><p> 至于为什么会选EMA,简单有效!我们其实可以发现其公式可以用线性插值函数快速实现,虽然没人说,但我觉得在很多时候我们平滑移动物体和相机用到这个插值基于的原理就是这个,在每帧不断迭代才产生了平滑。</p><script type="math/tex; mode=display">a_i=Mathf.Lerp(a_{i-1 },x_i,p_1)=a_{i-1}+(1-p_1)x_i=(1-p_1)a_{i-1}+p_1x_i</script><p><strong>3.实现</strong><br><a href="https://blog.csdn.net/qq_36490364/article/details/104515912">Unity下的实现</a></p>]]></content>
<categories>
<category>算法</category>
</categories>
<tags>
<tag>算法</tag>
</tags>
</entry>
<entry>
<title>求解最长回文子串</title>
<link href="/2020/03/11/%E6%B1%82%E8%A7%A3%E6%9C%80%E9%95%BF%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2/"/>
<url>/2020/03/11/%E6%B1%82%E8%A7%A3%E6%9C%80%E9%95%BF%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2/</url>
<content type="html"><![CDATA[<p><strong>描述</strong><br>  给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。</p><pre><code class="hljs c++">输入: <span class="hljs-string">"babad"</span>输出: <span class="hljs-string">"bab"</span>(注意: <span class="hljs-string">"aba"</span> 也是一个有效答案。)输入: <span class="hljs-string">"cbbd"</span>输出: <span class="hljs-string">"bb"</span></code></pre><p>  dp,除此之外还有一些很炫的解法,这里只讲dp这是一道很好的入门题。<br>  我一向认为dp第一步不是写状态转移方程,而是把状态设对。涉及到字符串的子串起码应该想到二维数组$dp<em>{ij}$,至于这个代表什么,就是手感了,设的好方程<del>推</del> (猜)的轻松,否则基本gg;<br>  这里直接给答案,$dp</em>{ij}$表示以$s<em>i开头至以s_j$结尾的串.如果$s_i…s_j$为回文串,那么$s</em>{i-1}…s<em>{j+1}$为回文串的条件就是$s</em>{i-1}==s_{j+1}$。</p><pre><code class="hljs c++">初始状态:dp[i][i]==<span class="hljs-literal">true</span>; i∈<span class="hljs-number">1.</span>..ndp[i][j]=dp[i+<span class="hljs-number">1</span>]d[j<span class="hljs-number">-1</span>]&&(s[i]==s[j])</code></pre><p>画表理解一下,以babad为例,填的顺序是斜着填的</p><div class="table-container"><table><thead><tr><th>1</th><th>0</th><th>1</th><th>0</th><th>0</th></tr></thead><tbody><tr><td></td><td>1</td><td>0</td><td>1</td><td>0</td></tr><tr><td></td><td></td><td>1</td><td>0</td><td>0</td></tr><tr><td></td><td></td><td></td><td>1</td><td>0</td></tr><tr><td></td><td></td><td></td><td></td><td>1</td></tr></tbody></table></div><img src="/2020/03/11/%E6%B1%82%E8%A7%A3%E6%9C%80%E9%95%BF%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2/20200311204401984.png" class="" title="在这里插入图片描述">上代码<pre><code class="hljs c++"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> {<span class="hljs-keyword">public</span>: <span class="hljs-function">string <span class="hljs-title">longestPalindrome</span><span class="hljs-params">(string s)</span> </span>{ <span class="hljs-type">int</span> len=s.<span class="hljs-built_in">length</span>(); vector<vector<<span class="hljs-type">bool</span>>> <span class="hljs-built_in">d</span>(len, <span class="hljs-built_in">vector</span><<span class="hljs-type">bool</span>>(len)); <span class="hljs-type">int</span> max=<span class="hljs-number">1</span>,p=<span class="hljs-number">0</span>,flag=<span class="hljs-number">1</span>; <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>;i<len;i++)<span class="hljs-comment">//初始化</span> d[i][i]=<span class="hljs-literal">true</span>; <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> j=<span class="hljs-number">1</span>;j<len;j++) { <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>;i<len-j;i++) { <span class="hljs-keyword">if</span>(j==<span class="hljs-number">1</span>)<span class="hljs-comment">//长度为2的串</span> { d[i][j+i]=(s[i]==s[i+<span class="hljs-number">1</span>]); <span class="hljs-keyword">if</span>(d[i][i+<span class="hljs-number">1</span>]&&flag)<span class="hljs-comment">//flag用于只记录第一个长度为2的回文串,不用也没关系</span> { max=<span class="hljs-number">2</span>; p=i; flag=<span class="hljs-number">0</span>; } } <span class="hljs-keyword">else</span><span class="hljs-comment">//长度大与2的串</span> { d[i][j+i]=d[i+<span class="hljs-number">1</span>][i+j<span class="hljs-number">-1</span>]&&(s[i]==s[i+j]);<span class="hljs-comment">//转移方程</span> <span class="hljs-keyword">if</span>(d[i][j+i]&&j+<span class="hljs-number">1</span>>max) { max=j+<span class="hljs-number">1</span>; p=i; } } } } <span class="hljs-keyword">return</span> s.<span class="hljs-built_in">substr</span>(p, max);<span class="hljs-comment">//从p开始切割max位</span> }};</code></pre>]]></content>
<categories>
<category>习题</category>
</categories>
<tags>
<tag>习题</tag>
</tags>
</entry>
<entry>
<title>Unity Mega Fiers导入PC2文件失败</title>
<link href="/2020/03/07/Unity-Mega-Fiers%E5%AF%BC%E5%85%A5PC2%E6%96%87%E4%BB%B6%E5%A4%B1%E8%B4%A5/"/>
<url>/2020/03/07/Unity-Mega-Fiers%E5%AF%BC%E5%85%A5PC2%E6%96%87%E4%BB%B6%E5%A4%B1%E8%B4%A5/</url>
<content type="html"><![CDATA[<p><strong>1.前言</strong><br>  如何导入看这篇文章,<a href="https://blog.csdn.net/woerxi/article/details/64922563?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task">链接</a>,讲的很清楚了<br><strong>2.问题及解决</strong><br>  出现mapping of XXX failed,查了好多资料,最后还是好不容易翻墙找到了<a href="http://www.west-racing.com/mf/?page_id=1335">官网</a>才知道,这个也太偏僻了</p><pre><code class="hljs plaintext">Make sure you are using local space point cache files, so for exampleif you are using Max make sure you use the Object Space Point Cache Modifier as opposed to the World Space version and in other packages be sure to click the local space option box in the exporters.Also if you have Mapping Failures check to see if the Optimize Meshoption is turned off in the Unity FBX import settings.</code></pre><p>第一种是坐标问题,我是第二种情况,这个勾去掉就ok</p><img src="/2020/03/07/Unity-Mega-Fiers%E5%AF%BC%E5%85%A5PC2%E6%96%87%E4%BB%B6%E5%A4%B1%E8%B4%A5/20200307193324247.png" class="" title="在这里插入图片描述"><p>还有,我补充一句,添加point cache组件的object一定是max里用点缓存的,也就是说,该是哪个物体就给哪个物体,这个不要给了父物体</p>]]></content>
<categories>
<category>Unity</category>
</categories>
<tags>
<tag>Unity</tag>
</tags>
</entry>
<entry>
<title>Unity使用导入FBX的自带动画产生位移</title>
<link href="/2020/03/06/Unity%E4%BD%BF%E7%94%A8%E5%AF%BC%E5%85%A5FBX%E7%9A%84%E8%87%AA%E5%B8%A6%E5%8A%A8%E7%94%BB%E4%BA%A7%E7%94%9F%E4%BD%8D%E7%A7%BB/"/>
<url>/2020/03/06/Unity%E4%BD%BF%E7%94%A8%E5%AF%BC%E5%85%A5FBX%E7%9A%84%E8%87%AA%E5%B8%A6%E5%8A%A8%E7%94%BB%E4%BA%A7%E7%94%9F%E4%BD%8D%E7%A7%BB/</url>
<content type="html"><![CDATA[<p><strong>1.描述</strong><br>  在使用3dsMax导出模型带的动画时,再导入Unity,模型会一直移动到一个固定点。<br><strong>2.解决</strong><br>  导出动画时不要选bake烘培,因为这个似乎是带着坐标信息的,我测试了好几次,把这个去掉就ok了。但是不选这个的画如果模型有做一些绑定物品,比如手中握着的东西在导入到unity时会产生一些小偏差,不过可以再重新绑定。</p><img src="/2020/03/06/Unity%E4%BD%BF%E7%94%A8%E5%AF%BC%E5%85%A5FBX%E7%9A%84%E8%87%AA%E5%B8%A6%E5%8A%A8%E7%94%BB%E4%BA%A7%E7%94%9F%E4%BD%8D%E7%A7%BB/20200306141331820.png" class="" title="在这里插入图片描述">]]></content>
<categories>
<category>Unity</category>
</categories>
<tags>
<tag>Unity</tag>
</tags>
</entry>
<entry>
<title>Unity使用NetworkServer与NetWorkClient实现服务端与客户端传递消息</title>
<link href="/2020/02/26/Unity%E4%BD%BF%E7%94%A8NetworkServer%E4%B8%8ENetWorkClient%E5%AE%9E%E7%8E%B0%E6%9C%8D%E5%8A%A1%E7%AB%AF%E4%B8%8E%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BC%A0%E9%80%92%E6%B6%88%E6%81%AF/"/>
<url>/2020/02/26/Unity%E4%BD%BF%E7%94%A8NetworkServer%E4%B8%8ENetWorkClient%E5%AE%9E%E7%8E%B0%E6%9C%8D%E5%8A%A1%E7%AB%AF%E4%B8%8E%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BC%A0%E9%80%92%E6%B6%88%E6%81%AF/</url>
<content type="html"><![CDATA[<h2 id="Unity使用NetworkServer与NetWorkClient实现服务端与客户端传递消息"><a href="#Unity使用NetworkServer与NetWorkClient实现服务端与客户端传递消息" class="headerlink" title="Unity使用NetworkServer与NetWorkClient实现服务端与客户端传递消息"></a>Unity使用NetworkServer与NetWorkClient实现服务端与客户端传递消息</h2><p><strong>1.环境</strong><br>  Unity2018<br><strong>2.前述</strong><br>  第一个想到的方法是使用Socket,也实现了功能,但是我想Unity应该有自带的API用来实现,可是查阅了好多资料,发现大多是使用NetWorkManager,鉴于我的项目对网络的要求十分微小,因此我选择了较为低级NetworkServer和NetWorkClient类<br><strong>3.实现</strong><br>  create一个Emty GameOjbect,挂载以下脚本,注意一下开启客户端方法setClient需要绑定在一个button上,服务端发送信息sendMessage也要绑定在一个button上。<br>  注意,这是从我项目中直接拷贝过来稍微改了一下,可能并不能直接使用,但是应该可以很好的帮助理解这两个类的大致用法,下面是两个文档,好像需要翻墙<br><a href="https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Networking.NetworkServer.html">NetworkServer文档</a><br><a href="https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Networking.NetworkClient.html">NetworkClient文档</a></p><p>服务端与客户端的实现<br><pre><code class="hljs c#"><span class="hljs-keyword">using</span> System.Net;<span class="hljs-keyword">using</span> System.Net.Sockets;<span class="hljs-keyword">using</span> UnityEngine;<span class="hljs-keyword">using</span> UnityEngine.Networking;<span class="hljs-keyword">using</span> UnityEngine.UI;<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MyNetWork</span> : <span class="hljs-title">MonoBehaviour</span>{ <span class="hljs-keyword">public</span> Text ipText; <span class="hljs-keyword">private</span> NetworkClient myClient; <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Start</span>()</span> { getIp(); } <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Update</span>()</span> { } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">getIp</span>()<span class="hljs-comment">//获取本机IP</span></span> { <span class="hljs-keyword">try</span> { IPHostEntry IpEntry = Dns.GetHostEntry(Dns.GetHostName()); <span class="hljs-keyword">foreach</span> (IPAddress item <span class="hljs-keyword">in</span> IpEntry.AddressList) { <span class="hljs-comment">//AddressFamily.InterNetwork ipv4</span> <span class="hljs-keyword">if</span> (item.AddressFamily == AddressFamily.InterNetwork) { ipText.text = <span class="hljs-string">"IP:"</span> + item.ToString(); MyMessage.ipAddress = item.ToString(); <span class="hljs-comment">//开启服务端</span> <span class="hljs-keyword">if</span>(NetworkServer.active) { NetworkServer.Shutdown(); } <span class="hljs-keyword">else</span> { starServer(); } <span class="hljs-keyword">return</span>; } } <span class="hljs-keyword">return</span> ; } <span class="hljs-keyword">catch</span> { <span class="hljs-keyword">return</span>; } } <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">starServer</span>()<span class="hljs-comment">//开启服务器</span></span> { NetworkServer.Listen(MyMessage.ipAddress, MyMessage.serverPort);<span class="hljs-comment">//参数为IP和Port</span> Debug.Log(NetworkServer.active); } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">sendMessage</span>()<span class="hljs-comment">//服务端发送消息</span></span> { <span class="hljs-keyword">if</span>(NetworkServer.connections.Count><span class="hljs-number">0</span>) NetworkServer.SendToAll(MyMessage.msgTypeSend, <span class="hljs-keyword">new</span> MessageInfo(isWalking, steps)); } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setClient</span>()<span class="hljs-comment">//开启客户端</span></span> { myClient = <span class="hljs-keyword">new</span> NetworkClient(); myClient.RegisterHandler(<span class="hljs-number">503</span>, receiveMessage);<span class="hljs-comment">//注册回调函数,参数1为消息类型,数字可以自定义,用于识别回调函数,参数2为回调函数</span> myClient.Connect(MyMessage.ipAddress, MyMessage.serverPort);<span class="hljs-comment">//连接服务端,参数分别为IP和Port</span> } <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">receiveMessage</span>(<span class="hljs-params">NetworkMessage netMsg</span>)<span class="hljs-comment">//客户端接收信息的回调函数的实现</span></span> { MessageInfo hostMessage = netMsg.ReadMessage<MessageInfo>(); <span class="hljs-built_in">bool</span> isWalking= hostMessage.isWalking; <span class="hljs-built_in">long</span> steps = hostMessage.stepCount; <span class="hljs-comment">//Output the Player name and comment</span> Debug.Log(<span class="hljs-string">"isWalking: "</span> + isWalking); Debug.Log(<span class="hljs-string">"steps : "</span> + steps); }}</code></pre><br>MessgafeInfo类是封装的用来发送信息的自定义实体类,继承MessageBase<br><pre><code class="hljs c#"><span class="hljs-keyword">using</span> UnityEngine.Networking;<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MessageInfo</span> : <span class="hljs-title">MessageBase</span>{ <span class="hljs-keyword">public</span> <span class="hljs-built_in">bool</span> isWalking; <span class="hljs-keyword">public</span> <span class="hljs-built_in">long</span> stepCount; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">MessageInfo</span>()</span> { } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">MessageInfo</span>(<span class="hljs-params"><span class="hljs-built_in">bool</span> iswalk,<span class="hljs-built_in">long</span> stepcount</span>)</span> { <span class="hljs-keyword">this</span>.isWalking = iswalk; <span class="hljs-keyword">this</span>.stepCount = stepcount; }}</code></pre></p>]]></content>
<categories>
<category>Unity</category>
</categories>
<tags>
<tag>Unity</tag>
</tags>
</entry>
<entry>
<title>Unity Android真机测试</title>
<link href="/2020/02/22/Unity-Android%E7%9C%9F%E6%9C%BA%E6%B5%8B%E8%AF%95/"/>
<url>/2020/02/22/Unity-Android%E7%9C%9F%E6%9C%BA%E6%B5%8B%E8%AF%95/</url>
<content type="html"><![CDATA[<h2 id="Unity-Android真机测试"><a href="#Unity-Android真机测试" class="headerlink" title="Unity Android真机测试"></a>Unity Android真机测试</h2><p>两种方法,首先在你需要debug的代码位置用Debug.log(“你想要的信息”)</p><h2 id="一-wifi"><a href="#一-wifi" class="headerlink" title="一.wifi"></a>一.wifi</h2><p>1.手机连数据线,要求电脑,手机同一网段,手机调试模式<br>2.adb tcpip 5555 //port<br>3.adb connect <ip><br>4.adb devices //检查是否连接<br>5.unity中build and run<br>6.unity中edit->preferences 找到sdk路径 复制 在我的电脑中打开 找到tools文件夹下的monitor打开,搜索栏输入:tag:Unity 观察</p><h2 id="二-usb"><a href="#二-usb" class="headerlink" title="二.usb"></a>二.usb</h2><p>1.手机连数据线,手机调试模式,允许usb 安装 (发现有些手机要有sim卡才能开启usb安装)<br>2.unity中build and run<br>3.unity中edit->preferences 找到sdk路径 复制 在我的电脑中打开 找到tools文件夹下的monitor打开,搜索栏输入:tag:Unity 观察<br>ps:其中第2步可用以下2步代替<br>①.unity打包出apk<br>②.cmd中adb -s <设备号> install -r <apk路径> //设备号可用adb devices 查看</p><p><strong>Montitor的界面</strong><br><img src="/2020/02/22/Unity-Android%E7%9C%9F%E6%9C%BA%E6%B5%8B%E8%AF%95/20200222155222186.png" class="" title="在这里插入图片描述">下面是通法,不仅仅适用于unity,可以测试任何apk,这是以前开发安卓时用到的,上面两种其实是特例罢了,<br>1.连数据线,手机调试模式,允许usb 安装<br>2.用android studio(或者其他IDE)打包出apk<br>3.adb -s <设备号> install -r <apk路径> //设备号可用adb devices 查看<br>4.找到sdk路径 复制 在我的电脑中打开 找到tools文件夹下的monitor打开,搜索栏输入:tag:<xxx> 观察,也可以是pid:<xxx>,app:<xxx>,text:<xxx></p><p>ps:以上所有<> 在输入的时候都不同打出来,我只为为了表示这里填的是一个值;<br>用wifi的方法我没试过,因为以前都是用usb连接的习惯了,如果第一种方法有失效请告知</p>]]></content>
<categories>
<category>Unity</category>
</categories>
<tags>
<tag>Unity</tag>
</tags>
</entry>
<entry>
<title>Unity打包apk后雾效果消失</title>
<link href="/2020/02/22/Unity%E6%89%93%E5%8C%85apk%E5%90%8E%E9%9B%BE%E6%95%88%E6%9E%9C%E6%B6%88%E5%A4%B1/"/>
<url>/2020/02/22/Unity%E6%89%93%E5%8C%85apk%E5%90%8E%E9%9B%BE%E6%95%88%E6%9E%9C%E6%B6%88%E5%A4%B1/</url>
<content type="html"><![CDATA[<h2 id="Unity打包apk后雾效果消失"><a href="#Unity打包apk后雾效果消失" class="headerlink" title="Unity打包apk后雾效果消失"></a>Unity打包apk后雾效果消失</h2><p>  今天测试一个雾的效果时,发现在电脑上play有用,等打包到apk时就失效了。讲一下避免以下坑吧。</p><p> <strong>1.环境</strong><br>unity2018</p><p><strong>2.解决方法</strong><br>Edit->Project Settings->Graphics 找到Shader Stripping 中fog mode设置为custom(原来是Automatic),然后选中你想要的模式,最后重新打包就ok</p><img src="/2020/02/22/Unity%E6%89%93%E5%8C%85apk%E5%90%8E%E9%9B%BE%E6%95%88%E6%9E%9C%E6%B6%88%E5%A4%B1/20200222150033160.png" class="" title="在这里插入图片描述"><p>这三种模式的介绍请看另一篇吧<br><a href="https://blog.csdn.net/qq_36490364/article/details/104444197">unity fog雾的三种模式</a></p>]]></content>
<categories>
<category>Unity</category>
</categories>
<tags>
<tag>Bug</tag>
<tag>Unity</tag>
</tags>
</entry>